What is the correct way to delay cancellation support inside a task execution delegate?

I don't see any specific mention of either MSDN or how to do this. The use case is somewhat unclear, but still let's assume what I suspect.

var cancel = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => { Task.Delay(1000, cancel.Token).Wait(); }, cancel.Token);
cancel.CancelAfter(100);
task.Wait();

      

The above code will attempt to cancel task

containing the child delay job detached after 100 milliseconds and wait for completion task

that will generate AggregateException

(due to cancellation). The problem is what task

becomes erroneous and not canceled. This is expected behavior because the delay task is not tied to the parent task

, even though both have the same cancellation token.

My question relates specifically to how you are going to connect Task.Delay

to a task that is already running. Is it possible to do this if you have access to the parent task? If this is not possible or not possible without accessing the parent instance of the task, what is the correct way to handle this scenario?

The best work I could think of was to end the delay task Wait

in a try / finally block and explicitly try to roll back the task cancellation.

try { Task.Delay(1000, cancel.Token).Wait(); } finally { cancel.Token.ThrowIfCancellationRequested(); }

      

While effective, it doesn't feel perfectly right, but I'm not sure if there is a better way to achieve this. The desired result is that the parent task jumps to Canceled

instead Faulted

if it fails. Therefore, if the genesis of cancellation occurs in the task of a separate child process, the parent task must still go to Canceled

.

NOTE . I forgot to asynchronously / await here on purpose, simply because it didn't change the problem or the result. If not, please provide an example.

+3


source to share


1 answer


Thus, a task is considered canceled when it OperationCanceledException

is thrown and fails inside it, and the associated one is CancellationToken

canceled.

In your case, an exception is thrown AggregateException

that contains TaskCanceledException

(which is OperationCanceledException

) instead TaskCanceledException

.

There is an easy way to fix this. Instead of a synchronous blocking with Task.Wait

that wraps any exceptions in a wrapper AggregateException

, you can use task.GatAwaiter().GetResult()

. This is what await

uses async-await

. It throws the original exception, and if there are more than one, it throws the first one:

var cancel = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => { Task.Delay(1000, cancel.Token).GetAwaiter().GetResult(); }, cancel.Token);
cancel.CancelAfter(100);
task.Wait();

      



If you used async-await

, you wouldn't have this problem, as unlike Task.Wait

it throws itself TaskCanceledException

:

var cancel = new CancellationTokenSource();
var task = Task.Run(() => Task.Delay(1000, cancel.Token), cancel.Token);
cancel.CancelAfter(100);
task.Wait();

      


I guess this is just an example. Real production code doesn't have to resemble this, since you block synchronously on an asynchronous operation, which in turn blocks synchronously on an asynchronous operation.

+1


source







All Articles