如何優雅地取消異步工作?

[英]How to cancel async work gracefully?


I have a UI application which logs something every second. Every time OpenFileHandler is fired, I must start logging to a new file. This is a very simplified version of the code:

我有一個UI應用程序,它每秒記錄一些東西。每次觸發OpenFileHandler時,我都必須開始記錄到新文件。這是代碼的一個非常簡化的版本:

string _logItem;
string _fileName;
CancellationTokenSource _cts;
Task _task;

private void OpenFileHandler(object sender, EventArgs e)
{
    // once OpenFileHandler has been fired, 
    // the _logItem should go to the new file

    if (_task != null && !_task.IsCompleted)
    {
        _cts.Cancel();
        // can't do: _task.Wait();
    }

    if ( _fileName != null )
    {
       _cts = new CancellationTokenSource();
       _task = LogAsync(_fileName, _cts.Token);
    }
}

private async Task LogAsync(string fileName, CancellationToken ct)
{
    using (var writer = new System.IO.StreamWriter(fileName, false))
    {
        try
        {
            while (true)
            {
                await Task.Delay(1000, ct);
                await writer.WriteLineAsync(_logItem);
            }
        }
        finally
        {
            writer.WriteLine("end of log!");
        }
    }
}

The problem: OpenFileHandler is synchronous, but I need to make sure the pending WriteLineAsync has completed and the old log file has been closed, before I can start a new LogAsync task.

問題:OpenFileHandler是同步的,但在開始新的LogAsync任務之前,我需要確保掛起的WriteLineAsync已完成並且舊的日志文件已關閉。

I cannot do _task.Wait() inside OpenFileHandler because it will block the UI thread.

我不能在OpenFileHandler中執行_task.Wait(),因為它會阻止UI線程。

I also cannot make OpenFileHandler an async method and do await _task inside it. That's because when the application is closed, and OpenFileHandler is fired with _fileName being null, I want the "end of log!" line to still be there in the log.

我也無法使OpenFileHandler成為異步方法,並在其中等待_task。那是因為當應用程序關閉,並且在_fileName為null的情況下觸發OpenFileHandler時,我想要“結束日志!”仍然在日志中的行。

How do I solve this?

我該如何解決這個問題?

2 个解决方案

#1


3  

As the first line of LogAsync, await the old task:

作為LogAsync的第一行,等待舊任務:

await prevTask;

You somehow need to pass in the previous task. Maybe as a method argument.

你不知何故需要傳遞上一個任務。也許作為方法論證。

You probably need to catch an OperationCancelledException, or:

您可能需要捕獲OperationCancelledException,或者:

await prevTask.ContinueWith(_ => { }); //suppress exceptions

#2


0  

Could you not handle the special case of shutting the application down (and null being passed to the OpenFileHandler method)?

你能不能處理關閉應用程序的特殊情況(並將null傳遞給OpenFileHandler方法)?

So in the case of the application shutting down then you _cts.Cancel() and then _task.Wait() to allow the logs to complete being written.

因此,在應用程序關閉的情況下,然后使用_cts.Cancel()然后_task.Wait()以允許日志完成寫入。

Rather than doing a while(true) can't you check if the CancellationToken has been cancelled or not, and if it has then exit and write the 'End of Log!'

你可以不檢查CancellationToken是否已被取消,而不是做了一段時間(真實),如果已經取消,則退出並寫下“結束日志!”


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2014/03/13/b086a91654bd02d02b06fb86ff6bee7a.html



 
  © 2014-2022 ITdaan.com 联系我们: