优雅处理任务取消 [英] Elegantly handle task cancellation

查看:126
本文介绍了优雅处理任务取消的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当使用任务执行需要能够取消的大型/长期运行的工作负载时,我经常使用类似于此操作的模板来执行任务:

When using tasks for large/long running workloads that I need to be able to cancel I often use a template similar to this for the action the task executes:

public void DoWork(CancellationToken cancelToken)
{
    try
    {
        //do work
        cancelToken.ThrowIfCancellationRequested();
        //more work
    }
    catch (OperationCanceledException)
    {
        throw;
    }
    catch (Exception ex)
    {
        Log.Exception(ex);
        throw;
    }
}

OperationCanceledException不应该被记录为错误,如果任务要转换到取消状态,不要吞咽。任何其他例外都不需要处理超出此方法的范围。

The OperationCanceledException should not be logged as an error but must not be swallowed if the task is to transition into the cancelled state. Any other exceptions do not need to be dealt with beyond the scope of this method.

这总是觉得有点笨重,视觉工作室默认会打破OperationCanceledException(尽管由于我使用了这种模式,我已经关闭了用户未处理的操作,因为使用了OperationCanceledException)。

This always felt a bit clunky, and visual studio by default will break on the throw for OperationCanceledException (Though I have 'break on User-unhandled' turned off now for OperationCanceledException because of my use of this pattern).

理想情况下,我想我想能够做这样的事情:

Ideally I think I'd like to be able to do something like this:

public void DoWork(CancellationToken cancelToken)
{
    try
    {
        //do work
        cancelToken.ThrowIfCancellationRequested();
        //more work
    }
    catch (Exception ex) exclude (OperationCanceledException)
    {
        Log.Exception(ex);
        throw;
    }
}

有一些排除列表适用于catch,但没有目前不可用的语言支持(@ eric-lippert:c#vNext feature:))。

i.e. have some sort of exclusion list applied to the catch but without language support that is not currently possible (@eric-lippert: c# vNext feature :)).

另一种方法通过延续:

public void StartWork()
{
    Task.Factory.StartNew(() => DoWork(cancellationSource.Token), cancellationSource.Token)
        .ContinueWith(t => Log.Exception(t.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
}

public void DoWork(CancellationToken cancelToken)
{
    //do work
    cancelToken.ThrowIfCancellationRequested();
    //more work
}

但我不太喜欢因为异常在技术上可能会有一个以上的单一内部异常,并且在第一个示例(如果我做的不仅仅是记录它)时,没有像日志记录异常一样多的上下文。

but I don't really like that as the exception technically could have more than a single inner exception and you don't have as much context while logging the exception as you would in the first example (if I was doing more than just logging it).

我明白这是一个风格的问题,但是想知道有没有人有任何更好的建议?

I understand this is a bit of a question of style, but wondering if anyone has any better suggestions?

我只需要坚持示例1?

Eamon

推荐答案

那么,问题?只要丢掉 catch(OperationCanceledException)块,并设置适当的延续:

So, what's the problem? Just throw away catch (OperationCanceledException) block, and set proper continuations:

var cts = new CancellationTokenSource();
var task = Task.Factory.StartNew(() =>
    {
        var i = 0;
        try
        {
            while (true)
            {
                Thread.Sleep(1000);

                cts.Token.ThrowIfCancellationRequested();

                i++;

                if (i > 5)
                    throw new InvalidOperationException();
            }
        }
        catch
        {
            Console.WriteLine("i = {0}", i);
            throw;
        }
    }, cts.Token);

task.ContinueWith(t => 
        Console.WriteLine("{0} with {1}: {2}", 
            t.Status, 
            t.Exception.InnerExceptions[0].GetType(), 
            t.Exception.InnerExceptions[0].Message
        ), 
        TaskContinuationOptions.OnlyOnFaulted);

task.ContinueWith(t => 
        Console.WriteLine(t.Status), 
        TaskContinuationOptions.OnlyOnCanceled);

Console.ReadLine();

cts.Cancel();

Console.ReadLine();

TPL区分取消和故障。因此,取消(即在任务主体内抛出 OperationCancelledException )不是故障。

TPL distinguishes cancellation and fault. Hence, cancellation (i.e. throwing OperationCancelledException within task body) is not a fault.

主要内容:不要处理任务主体中的异常,而不会重新抛出它们。

The main point: do not handle exceptions within task body without re-throwing them.

这篇关于优雅处理任务取消的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆