Is ContinueWith guaranteed to execute?

2019-05-03 07:35发布

问题:

I've got a bit of code that is acting as a light weight, non-blocking, critical section. I am hoping that no matter what happens with _func and cancellationToken in the Task.Run clause, that the continuation is guaranteed to run such that the Exit statement in its finally block will always execute.

Is it safe to assume that the finally block below, short of catastrophic failure in the process, will be executed with roughly the same guarantees that finally normal operates with?

if (Enter())
{
    Task.Run<T>(_func, cancellationToken)
        .ContinueWith((antecedent) =>
        {
            try
            {
                antecedent.Wait(cancellationToken);
                Interlocked.Exchange<T>(ref _result, antecedent.Result);
            }
            catch (AggregateException e)
            {
                Interlocked.Exchange(ref _exceptions, e);
            }
            catch (OperationCanceledException)
            {
                ResetState();
            }
            catch (Exception e)
            {
                Interlocked.Exchange(ref _exceptions, new AggregateException(e));
            }
            finally
            {
                Exit();
            }
        })
}

回答1:

Based on the MSDN documentation:

The returned Task will not be scheduled for execution until the current task has completed, whether it completes due to running to completion successfully, faulting due to an unhandled exception, or exiting out early due to being canceled.

So, unless _func never returns (runs in an infinite loop for example), then the continuation will always run.

And when the continuation runs, the finally block is guaranteed to execute as per the rules of C#.

So the answer is yes (unless of course _func never returns).



回答2:

If the token is canceled before the first task runs, the continuation will not run, and thus the finally will not execute. Tested here with VS 2015: http://pastebin.com/9qmUYnqv

Incidentally, there are some design issues with the code as written -- having tasks modify state is not the best idea; the tasks should return values and whatnot. In this particular case, for a nonblocking critical sections, consider using SemaphoreSlim.