TaskCompletionSource - Trying to understand threadless async work
Solution 1
TaskCompletionSource
is used to create Task
objects that don't execute code.
They're used quite a bit by Microsoft's new async APIs - any time there's I/O-based asynchronous operations (or other non-CPU-based asynchronous operations, like a timeout). Also, any async Task
method you write will use TCS to complete its returned Task
.
I have a blog post Creating Tasks that discusses different ways to create Task
instances. It's written from an async
/await
perspective (not a TPL perspective), but it still applies here.
Also see Stephen Toub's excellent posts:
- The Nature of TaskCompletionSource
- Mechanisms for Creating Tasks
-
await anything; (using
TaskCompletionSource
toawait
anything). -
Using Tasks to implement the APM Pattern (creating
Begin
/End
usingTaskCompletionSource
).
Solution 2
I like the explanation which was provided in http://tutorials.csharp-online.net/TaskCompletionSource
(sorry, the link may be dead at the moment)
First two paragraphs are below
We've seen how Task.Run creates a task that runs a delegate on a pooled (or non-pooled) thread. Another way to create a task is with TaskCompletionSource.
TaskCompletionSource lets you create a task out of any operation that starts and finishes some time later. It works by giving you a "slave" task that you manually drive—by indicating when the operation finishes or faults. This is ideal for I/O- bound work: you get all the benefits of tasks (with their ability to propagate return values, exceptions, and continuations) without blocking a thread for the duration of the operation.
To use TaskCompletionSource, you simply instantiate the class. It exposes a Task property that returns a task upon which you can wait and attach continuations—just as with any other task. The task, however, is controlled entirely by the TaskCompletionSource object via the following methods:
public class TaskCompletionSource<TResult>
{
public void SetResult(TResult result);
public void SetException (Exception exception);
public void SetCanceled();
public bool TrySetResult (TResult result);
public bool TrySetException (Exception exception);
public bool TrySetCanceled();
...
}
Calling any of these methods signals the task, putting it into a completed, faulted, or canceled state (we'l cover the latter in the section "Cancellation"). You'e supposed to call one of these methods exactly once: if called again, SetResult, SetException, or SetCanceled will throw an exception, whereas the Try* methods return false.
The following example prints 42 after waiting for five seconds:
var tcs = new TaskCompletionSource<int>();
new Thread (() => {
Thread.Sleep (5000);
tcs.SetResult (42);
})
.Start();
Task<int> task = tcs.Task; // Our "slave" task.
Console.WriteLine(task.Result); // 42
Other interesting quotes
The real power of TaskCompletionSource is in creating tasks that don't tie up threads.
.. and later on
Our use of TaskCompletionSource without a thread means that a thread is engaged only when the continuation starts, five seconds later. We can demonstrate this by starting 10,000 of these operations at once without error or excessive resource consumption:
coding4fun
Updated on April 09, 2020Comments
-
coding4fun about 4 years
I'm trying to understand the purpose of
TaskCompletionSource
and its relation to async/threadless work. I think I have the general idea but I want to make sure my understanding is correct.I first started looking into the Task Parallel Library (TPL) to figure out if there was a good way to create your own threadless/async work (say you're trying to improve scalability of your ASP.NET site) plus understanding of the TPL looks like it will be very important in the future (
async
/await
). Which led me to theTaskCompletionSource
.From my understanding it looks like adding
TaskCompletionSource
to a one of your classes doesn't really do much in as making your coding async; if you're still executing sync code then the call to your code will block. I think this is even true of microsoft APIs. For example, say inDownloadStringTaskAsync
off ofWebClient
class, any setup / sync code they are doing initially will block. The code you're executing has to run on some thread, either the current thread or you will have to spin off a new one.So you use
TaskCompletionSource
in your own code when you're calling otherasync
calls from Microsoft so the client of your classes doesn't have to create a new thread for your class to not block.Not sure how Microsoft does their async APIs internally. For example, there is a new
async
method off of theSqlDataReader
for .Net 4.5. I know there is IO Completion Ports. I think it's a lower level abstraction (C++?) that probably most C# developers won't use. Not sure if IO completion Ports will work for Database or network calls (HTTP) or if its just used for file IO.So the question is, am I correct in my understanding correct? Are there certain things I've represented incorrectly?