[英]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.