[英]Cancel and wait on async Task methods
试图弄清楚这一点,但找不到任何确定的东西。
假设我有一个 class 可以异步执行一些工作并且可以即时启动或停止:
class ModelObject
{
private CancellationTokenSource cts;
private CancellationToken ct;
private Progress<int> progress;
private async Task DoSomething(IProgress<int> progress)
{
...
while (DoingSomething)
{
if (ct.IsCancellationRequested)
{
await Task.Delay(10000); // Take our sweet time to cancel
// Cancel work and return
}
...
}
}
public async void StartAsync()
{
progress = new Progress<int>(...);
cts = new CancellationTokenSource();
ct = cts.Token;
await DoSomething(progress);
}
public void Stop()
{
ct.Cancel();
// HELP: await while DoSomething cancels
// perform cleanup
}
}
当现有的DoSomething
实例取消时,我不确定如何await
。
典型的用法是这样的:
ModelObject modelObject = new ModelObject();
modelObject.StartAsync();
modelObject.Stop();
任何帮助将不胜感激。
虽然您可以等待从DoSomething
返回的任务,但您也可以使用TaskCompletionSource
;
private TaskCompletionSource<object> tcs; private async Task DoSomething(IProgress<int> progress){ ... while (DoingSomething) { if (ct.IsCancellationRequested) { // Cancel work and return } ... } tcs.SetResult(null); } public async Task Stop () { cts.Cancel(); await tcs.Task; }
我的偏好是将Completion
属性添加到ModelObject
class。 调用者可以在取消操作后选择await
或不等待Completion
。 此外,我可能会防范不当使用 API。 在现有操作挂起时尝试启动新操作会导致异常。
public Task Completion { get; private set; } = Task.CompletedTask;
public async void Start()
{
if (_cts != null) throw new InvalidOperationException("Operation is in progress.");
using (_cts = new CancellationTokenSource())
{
_ct = _cts.Token;
_progress = new Progress<int>(/*...*/);
this.Completion = DoSomethingAsync();
try
{
await this.Completion;
}
catch { } // Ignore
finally
{
_cts = null;
_ct = default;
_progress = null;
}
}
}
public void Stop()
{
_cts?.Cancel();
}
另一条建议:如果取消,请确保您的DoSomethingAsync
抛出OperationCanceledException
。 这是可取消异步操作的预期行为。 例如,如果您将令牌传递给任何内置的 API,您会注意到返回的任务将在调用_cts.Cancel()
方法后立即以Canceled
的形式完成。 为了模仿这种行为,您可以在循环内调用_ct.ThrowIfCancellationRequested()
,并将令牌传递给任何接受它的 API(例如Task.Delay
方法)。 事实上,在内部捕获这个异常可能会更好,并在 catch 块内进行清理:
private async Task DoSomethingAsync()
{
try
{
while (doingSomething)
{
// Do some work
_ct.ThrowIfCancellationRequested();
}
}
catch (OperationCanceledException)
{
await Task.Delay(10000); // Take our sweet time to cancel
throw; // Rethrow the exception
}
finally
{
// perform cleanup, whether successful, failed or canceled
}
}
我不确定为什么StartAsync
方法是异步的,因为除非调用Stop
,否则它会无限期地执行。 我的意思是如果不采取其他行动,它不会在某个时间点返回。 我觉得在这个 class 中不需要async
但是如果有必要,我已经起草了可能有帮助的代码
class ModelObject {
private CancellationTokenSource cts;
private CancellationToken ct;
private Progress<int> progress;
private Task doSomethingTask;
private async Task DoSomething(IProgress<int> progress){
...
while (DoingSomething)
{
if (ct.IsCancellationRequested)
{
// Cancel work and return
}
...
}
}
public async void StartAsync () {
progress = new Progress<int>(...);
cts = new CancellationTokenSource ();
ct = cts.Token;
doSomethingTask = DoSomething (progress);
await doSomethingTask;
}
public void Stop () {
cts.Cancel();
doSomethingTask.Wait();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.