开发者

C# async CTP - How do I mark an async task as canceled without throwing a TaskCanceledException?

I have a short async task that will frequently need to be canceled after it has started. The "Task" class has an IsCanceled indicator which I think would be convenient to use to indicate that the async task was canceled without running to completion, but as far as I can tell the only way to have an async Task be marked as canceled is to throw a TaskCanceledException in the async function. Throwing an exception routinely, to indicate a situation which occurs unexceptionally, goes against how I understand exceptions should be used. Does anyone know a better way to indicate an async task to be canceled when it is expected to happen frequently?

My next-b开发者_C百科est alternative is to return a structure that has it's own IsCanceled property:

(I've ignored some good coding and style practices for brevity here)

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
}

But this seems redundant and harder to use. Not to mention it introduces consistency problems because there will be two IsCanceled's (one in the Task and one in the MightBeCanceled).


Usually the purpose of Cancellation is for the invoker of the asynchronous action to tell the action that it should stop processing. For this you pass a CancellationToken into the asynchronous method. That is why awaiting a Task that sets IsCancelled throws itself. It's meant an exceptional signal to an externally provoked action. Task's Cancellation shouldn't be used for control flow, but only to give an async action the option to finish early should the awaiting party have signaled that it no longer wants the result.

If you asynchronous actions are cancelled internally, you've already overloaded the concept and I would say that that reflecting that difference in cancellation is worthwhile and should be a property on the result container similar to the way you proposed it. But instead of calling it MightBeCancelled<T>, maybe something like InternallyCancellableResult<T>, to reflect the difference of cancellation concepts.


The problem is that the exception will be unobserved until you choose to wait on it. So the act of calling await means you will eventually observe the exception at some point when the final value is returned or if you wait for the task to complete. However, if you never care (it's a fire and forget task), then the exception is a non issue (for the most part).

I too, find this a little odd, always having to try / catch the task to handle aggregate exceptions. However, think about it a little bit:

 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;
       });
 }

It's not too far off. In many ways, I think of canceling another task and how would I do it? ThreadAbortException? It doesnt seem so far fetched to simply throw a specific exception on cancellation.

This is pretty much the pattern I have seen advocated in multiple places for how to handle cancellation.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜