问题描述
我有一个长期的计算,取决于输入值。如果在运行计算时更改了输入值,则应取消当前计算,并在上一个计算完成后重新开始计算。
基本思想如下:
Task _latestTask = Task.CompletedTask;
CancellationTokenSource _cancellationTokenSource;
int Value
{
get => _value;
set
{
_value = value;
UpdateCalculation();
}
}
void UpdateCalculation()
{
_cancellationTokenSource?.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = _cancellationTokenSource.Token;
var newTask = new Task(() => DoCalculation(Value,cancellationToken));
_latestTask.ContinueWith(antecedent => newTask.Start(),cancellationToken);
_latestTask = newTask;
}
但是,我发现根据设置Value
的频率,可能会在开始新任务之前取消继续任务。整个任务链停止了。
我应该如何组织事情,以使更改后的值导致当前的计算被放弃,并且一旦我知道上一个任务已经完成,便开始新的计算?
解决方法
您可以在此处重构UpdateCalculation
方法,以用更新的异步/等待技术替换旧的ContinueWith
,并确保最终CancellationTokenSource
被采用处置。
async void UpdateCalculation()
{
_cancellationTokenSource?.Cancel();
using (var cts = new CancellationTokenSource())
{
_cancellationTokenSource = cts;
var previousTask = _latestTask;
var newTask = new Task(() => DoCalculation(Value,cts.Token),cts.Token);
_latestTask = newTask;
// Prevent an exception from any task to crash the application
// It is possible that the newTask.Start() will throw too
try { await previousTask } catch { }
try { newTask.Start(); await newTask; } catch { }
// Ensure that the CTS will not be canceled after is has been disposed
if (_cancellationTokenSource == cts) _cancellationTokenSource = null;
}
}
此实现不是线程安全的。它要求始终从UI线程调用UpdateCalculation
。
documentation发出强烈警告,指出必须处置CancellationTokenSource
:
在释放对
Dispose
的最后引用之前,请始终致电CancellationTokenSource
。否则,除非垃圾回收器调用CancellationTokenSource
对象的Finalize
方法,否则将不会释放正在使用的资源。