簡體   English   中英

取消並等待異步任務方法

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM