[英]Cancel and wait on async Task methods
Trying to figure this out but can't find anything definitive.试图弄清楚这一点,但找不到任何确定的东西。
Suppose I have a class that does some work async and can be started or stopped on the fly:假设我有一个 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
}
}
I am not sure how to await
while the existing instance of DoSomething
cancels.当现有的DoSomething
实例取消时,我不确定如何await
。
Typical use would be like this:典型的用法是这样的:
ModelObject modelObject = new ModelObject();
modelObject.StartAsync();
modelObject.Stop();
Any help would be highly appreciated.任何帮助将不胜感激。
While you could await the task returned from DoSomething
, you can also use a TaskCompletionSource
;虽然您可以等待从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; }
My preference would be to add a Completion
property to the ModelObject
class.我的偏好是将Completion
属性添加到ModelObject
class。 The caller could choose to await
or not the Completion
after canceling an operation.调用者可以在取消操作后选择await
或不等待Completion
。 Also I would probably place guards against an improper use of the API.此外,我可能会防范不当使用 API。 Attempting to start a new operation while an existing one is pending, would cause an exception.在现有操作挂起时尝试启动新操作会导致异常。
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();
}
Another piece of advice: in case of cancellation ensure that your DoSomethingAsync
throws an OperationCanceledException
.另一条建议:如果取消,请确保您的DoSomethingAsync
抛出OperationCanceledException
。 This is the expected behavior of a cancelable asynchronous operation.这是可取消异步操作的预期行为。 For example if you pass the token to any built-in API, you'll notice that the returned task will complete as Canceled
immediately after calling the _cts.Cancel()
method.例如,如果您将令牌传递给任何内置的 API,您会注意到返回的任务将在调用_cts.Cancel()
方法后立即以Canceled
的形式完成。 To mimic this behavior you could call _ct.ThrowIfCancellationRequested()
inside the loop, and pass the token to any API that accepts it (for example the Task.Delay
method).为了模仿这种行为,您可以在循环内调用_ct.ThrowIfCancellationRequested()
,并将令牌传递给任何接受它的 API(例如Task.Delay
方法)。 In fact it would be probably better to catch internally this exception, and do the clean up inside the catch block:事实上,在内部捕获这个异常可能会更好,并在 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
}
}
I'm not sure why StartAsync
method is async since it indefinitely executes unless the Stop
is Invoked.我不确定为什么StartAsync
方法是异步的,因为除非调用Stop
,否则它会无限期地执行。 I mean it will not return at some point in time without another action.我的意思是如果不采取其他行动,它不会在某个时间点返回。 I feel there is no need for async
in this class But if it necessary, I've drafted code that might help我觉得在这个 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.