How to cancel async work gracefully?

喜你入骨 提交于 2019-12-25 04:27:23

问题


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:

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.

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

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.

How do I solve this?


回答1:


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

await prevTask;

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

You probably need to catch an OperationCancelledException, or:

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



回答2:


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

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.

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!'



来源:https://stackoverflow.com/questions/22377821/how-to-cancel-async-work-gracefully

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!