6

我有一个简短的异步任务,它开始后经常需要取消。“任务”类有一个 IsCanceled 指示器,我认为可以方便地使用它来指示异步任务在没有运行完成的情况下被取消,但据我所知,将异步任务标记为已取消的唯一方法是在异步函数中抛出 TaskCanceledException。定期抛出异常以指示异常发生的情况,这与我对应该使用异常的理解背道而驰。有谁知道一种更好的方法来指示异步任务在预期频繁发生时被取消?

我的下一个最佳选择是返回一个具有自己的 IsCanceled 属性的结构:

(为了简洁起见,我忽略了一些好的编码和风格实践)

class MightBeCanceled<T>
{
    public readonly T Value;
    public readonly bool IsCanceled;

    public MightBeCanceled(T value) { Value = value; IsCanceled = false; }
    public static MightBeCanceled<T> Canceled = new MightBeCanceled<T>(default(T), true);
    private MightBeCanceled(T value, bool isCanceled) { Value = value; IsCanceled = isCanceled; }
}

...

static async Task<MightBeCanceled<int>> Foo()
{
    if (someCancellationCondition)
        return MightBeCanceled<int>.Canceled;
    else
        return new MightBeCanceled<int>(42);
}

static async void Bar() 
{
    var mightBeCanceled = await Foo();

    if (mightBeCanceled.IsCanceled)
        ; // Take canceled action
    else
        ; // Take normal action
}

但这似乎多余且难以使用。更不用说它引入了一致性问题,因为会有两个 IsCanceled(一个在 Task 中,一个在 MightBeCanceled 中)。

4

2 回答 2

2

问题是,除非您选择等待,否则不会观察到异常。因此,调用的行为await意味着您最终会在返回最终值或等待任务完成时的某个时刻观察到异常。但是,如果您从不关心(这是一项“一劳永逸”的任务),那么例外情况就不是问题(在大多数情况下)。

我也觉得这有点奇怪,总是不得不尝试/捕获任务来处理聚合异常。但是,稍微考虑一下:

 try
 {
     var myResult = await Foo();

     // Do Success Actions Here... 
 }
 catch(AggregateException e)
 {
     e.Flatten().Handle(ex =>
       {
           if(ex is OperationCanceledException)
           {
               // Do Canceled Thing Here
               return true;
           }

           return false;
       });
 }

这不是太远了。在很多方面,我想取消另一个任务,我会怎么做?ThreadAbortException? 简单地在取消时抛出一个特定的异常似乎并不牵强。

这几乎是我在多个地方看到的关于如何处理取消的模式。

于 2011-08-11T21:19:38.833 回答
2

通常,取消的目的是让异步操作的调用者告诉操作它应该停止处理。为此,您将 a 传递给CancellationToken异步方法。这就是为什么等待设置 IsCancelled 的 Task 会抛出自身。这意味着对外部挑衅行动的异常信号。任务的取消不应该用于控制流,而只是为了让异步操作选择提前完成,如果等待方已经表示它不再想要结果。

如果您在内部取消了异步操作,那么您已经重载了这个概念,我会说反映取消的差异是值得的,并且应该是结果容器上的一个属性,类似于您提出的方式。但与其称它为MightBeCancelled<T>,也许类似InternallyCancellableResult<T>,以反映取消概念的差异。

于 2011-08-22T15:31:49.000 回答