您可能需要了解asynchronous programming with async/await 和cooperative cancellation。沟通取消的标准做法是抛出OperationCanceledException。可以取消的方法接受CancellationToken 作为参数,并经常观察令牌的IsCancellationRequested 方法。所以这里是一个带有布尔结果的可取消Validate 方法的基本结构:
bool Validate(CancellationToken token)
{
for (int i = 0; i < 50; i++)
{
// Throw an OperationCanceledException if cancellation is requested
token.ThrowIfCancellationRequested();
Thread.Sleep(100); // Simulate some CPU-bound work
}
return true;
}
CancellationToken 的“驱动程序”是一个名为 CancellationTokenSource 的类。在您的情况下,您必须创建此类的多个实例,每次更改图表时创建一个。您必须将它们存储在某个地方,以便以后可以调用它们的Cancel 方法,因此让我们在Form 中创建两个私有字段,一个用于最新的CancellationTokenSource,一个用于最新的验证Task:
private Task<bool> _validateTask;
private CancellationTokenSource _validateCTS;
最后,您必须为Diagram_Changed 事件的事件处理程序编写逻辑。可能不希望同时运行多个验证任务,因此最好在启动新任务之前发送await 以完成前一个任务。等待任务不会阻塞 UI,这一点很重要。这引入了复杂性,多个Diagram_Changed 事件以及其他不相关的事件可能在处理程序内的代码完成之前发生。幸运的是,您可以依靠 UI 的单线程特性,而不必担心通过多个异步工作流访问 _validateTask 和 _validateCTS 字段的线程安全性。您确实需要注意,在每个 await 之后,这些字段可能包含与 await 之前不同的值。
private async void Diagram_Changed(object sender, EventArgs e)
{
bool validationResult;
using (var cts = new CancellationTokenSource())
{
_validateCTS?.Cancel(); // Cancel the existing CancellationTokenSource
_validateCTS = cts; // Publish the new CancellationTokenSource
if (_validateTask != null)
{
// Await the completion of the previous task before spawning a new one
try { await _validateTask; }
catch { } // Ignore any exception
}
if (cts != _validateCTS) return; // Preempted (the event was fired again)
// Run the Validate method in a background thread
var task = Task.Run(() => Validate(cts.Token), cts.Token);
_validateTask = task; // Publish the new task
try
{
validationResult = await task; // Await the completion of the task
}
catch (OperationCanceledException)
{
return; // Preempted (the validation was canceled)
}
finally
{
// Cleanup before disposing the CancellationTokenSource
if (_validateTask == task) _validateTask = null;
if (_validateCTS == cts) _validateCTS = null;
}
}
// Do something here with the result of the validation
}
Validate 方法不应包含任何 UI 操作代码,因为它将在后台线程中运行。对 UI 的任何影响都应该在方法完成后通过验证任务的返回结果发生。