Configuring the continuation of the TaskCompletionSource task

Consider the following code:

public Task SomeAsyncMethod() { var tcs = new TaskCompletionSource(); ... do something, NOT setting the TaskCompletionSource... return tcs.Task } public void Callsite1() { await SomeAsyncMethod(); Debug.WriteLine(Thread.CurrentThread.ManagedThreadId); } public void Callsite2() { SomeAsyncMethod().ContinueWith((task) => { Debug.WriteLine(Thread.CurrentThread.ManagedThreadId); }); } 

At some point in time, the TaskCompletion source created in SomeAsyncMethod is installed in ThreadPool Thread:

 Debug.WriteLine(Thread.CurrentThread.ManagedThreadId); tcs.SetResult(); 

When Task expects from a TaskCompletionSource object (as in Callsite1), the continuation is performed synchronously in a thread called SetResult. When ContinueWith is called (as in Callsite2), the continuation is performed asynchronously in another thread.

This does not help call configure wait, as in

 public void Callsite1() { await SomeAsyncMethod().ConfigureAwait(true or false); } 

and it’s not even a question of this question. As a developer of SomeAsyncMethod, I don't want to call some potentially unknown code by calling SetResult. I want the continuation to always be scheduled asynchronously. And I can’t rely on the caller to properly configure the wait (if that even works).

How to configure TaskCompletionSource so that its task does not execute its continuation synchronously when it was waiting?

+4
source share
3 answers

There is no way to prevent the continuation of synchronous tasks. This is usually not a problem.

However, there are situations when you need it, for example, if you perform a task while holding a lock. In such cases, you can simply Task.Run complete the task:

 // Set the result on a threadpool thread, so any synchronous continuations // will execute in the background. Task.Run(() => tcs.TrySetResult(result)); // Wait for the TCS task to complete; note that the continuations // may not be complete. tcs.Task.Wait(); 

This is an advanced method. This is an exception to the guideline "Do not block async code ( async until the end)," as outlined in my blog .

This is part of my AsyncEx library as an extension method:

 public static void TrySetResultWithBackgroundContinuations<TResult>( this TaskCompletionSource<TResult> @this, TResult result); 

This method was first published by Stephen Tube on his blog .

+8
source

How to configure TaskCompletionSource so that its task does not execute its continuation synchronously when it was waiting?

It cannot be. You publish Task publicly, and as soon as you do this, everyone can freely attach a synchronous continuation (they just need to use another ContinueWith overload, they don't need to use async / await).

+2
source

Starting with .NET 4.6, TaskCreationOption.RunContinuationsAsynchronously been added for this view or case only:

[It forces] continuations added to the current task running asynchronously.

So you can create a TaskCompletionSource with TaskCreationOptions.RunContinuationsAsynchronously and make sure that calling it .SetResult() will not execute synchronous random code.

 var tcs = new TaskCompletionSource<bool>( TaskCreationOptions.RunContinuationsAsynchronously); // some time later, somewhere else tcs.SetResult(true); DoMoreStuffWithoutWorryingThatSetResultJustRanRandomCode(); 

For more information, see Stephen Toub's Blog Post, "New Task APIs in .NET 4.6 . "

When a task created with this option completes, it doesn’t even try to invoke continuations synchronously ... it will simply invoke all continuations asynchronously, as if no one had asked to execute synchronously, if possible.

There's also TaskContinuationOptions.RunContinuationsAsynchronously if you need similar behavior for things like .ContinueWith() .

[ TaskContinuationOptions.RunContinuationsAsynchronously indicates] that the continuation task should run asynchronously. This parameter takes precedence over TaskContinuationOptions.ExecuteSynchronously.

0
source

All Articles