When should TaskCompletionSource T be used?
I mostly use it when only an event based API is available (for example Windows Phone 8 sockets):
public Task<Args> SomeApiWrapper()
{
TaskCompletionSource<Args> tcs = new TaskCompletionSource<Args>();
var obj = new SomeApi();
// will get raised, when the work is done
obj.Done += (args) =>
{
// this will notify the caller
// of the SomeApiWrapper that
// the task just completed
tcs.SetResult(args);
}
// start the work
obj.Do();
return tcs.Task;
}
So it's especially useful when used together with the C#5 async
keyword.
TaskCompletionSource - Trying to understand threadless async work
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
).
Should I always complete a TaskCompletionSource?
Do I need to do any sort of cleanup (e.g. make sure
tcs.SetCancelled()
is called)?
Using a TaskCompletionSource<T>
doesn't require any cleanup. In fact, it doesn't even admit any cleanup. So the answer to your question is "no."
The TaskCompletionSource<T>
is just a conceptually simple data structure that allows you to push in at most a single thing (a result of type T
, an exception, or a cancellation). Its Task
property exposes a Task<T>
that is just a wrapper around this promised single thing that will be pushed into the TaskCompletionSource<T>
at some time in the future. It does not use the task pool whatsoever.
To never push anything into a TaskCompletionSource<T>
is perfectly valid. This just corresponds to a Task<T>
that will "run" forever and never complete.
TaskCompletionSource usage
What would I receive in result variable if completion source was cancelled?
You code will throw an OperationCancelledException
when awaiting a cancelled task. So the result variable will never be set.
You can handle the exception with a try/catch
block:
async Task SomeMethod()
{
try
{
.....
Run();
var result = await GetResult();
}
catch(OperationCancelledException)
{
// handle cancelled operation
}
}
Also, SomeMethod
should return a Task
as void
returning async
methods are usually only appropriate for event handlers as they must return void
. I blog about it briefly here.
In general, if you want an operation to be cancelable you pass in a CancellationToken
which the operation must check and pass on to other operations it kicks off. So you pass it all the way down the chain and into your callback.
You can also register a callback with the CancellationToken
that cancels the TaskCompletionSource
when the token is cancelled so you don't need to do it in your method.
void Run()
{
var cts = new CancellationTokenSource();
var myCompletionSource= new TaskCompletionSource();
cts.Token.Register(() => myCompletionSource.SetCancelled());
TriggerSomeLongLastingLogicWhichWillCallCallBackBelow(cts.Token);
}
void SomeCallback(CancellationToken token)
{
// do some work
....
token.ThrowIfCancellationRequested();
if (someCondition)
{
myCompletionSource.SetResult(<someResult>);
}
else
{
myCompletionSource.SetException(new Exception("error occcured"));
}
}
TaskCompletionSource : When to use SetResult() versus TrySetResult(), etc
I suspect the point is that if there's only one thing which will be setting the result, just call SetResult
etc. If you end up calling SetResult
twice, that indicates a bug. (Likewise if the TaskCompletionSource
has been disposed.)
If you've got several threads which could all be trying to set the result at the same time (e.g. it's there to indicate the first result out of several parallel web service calls) then use TrySetResult
, as it's entirely reasonable for multiple threads to "try" to set the result, unaware of whether another thread has already set it.
I've not seen any official guidance on it, but that would make sense.
Should I return ValueTask if I still use TaskCompletionSource to implement asynchrony?
There are pros and cons of each. In the "pro" column:
- When returning a result synchronously (i.e.
Task<T>
), usingValueTask<T>
avoids an allocation of the task - however, this doesn't apply for "void" (i.e. non-genericTask
), since you can just returnTask.CompletedTask
- When you are issuing multiple sequential awaitables, you can use a single "value task source" with sequential tokens to reduce allocations
(a special-case of "2" might be amortization via tools like PooledAwait
)
It doesn't feel like either of those apply here, but: if in doubt, remember that it is allocation-free to return a Task
as a ValueTask
(return new ValueTask(task);
), which would allow you to consider changing the implementation to an allocation-free one later without breaking the signature. You still pay for the original Task
etc of course - but exposing them as ValueTask
doesn't add any extra.
In all honesty, I'm not sure I'd worry too much about this in this case, since a delay is ways going to be allocatey.
TaskCompletionSource usage in IO Async methods
Note that for a definitive answer, you would have to ask the author of the code. Barring that, we can only speculate. However, I think it's reasonable to make some inferences with reasonable accuracy…
What is the point of using TaskCompletionSource here and why not to return the task created by Task.Factory.FromAsync() directly?
In this case, it appears to me that the main reason is to allow the implementation to deregister the registered callback CancelIgnoreFailure()
before the task is actually completed. This ensures that by the time the client code receives completion notification, the API itself has completely cleaned up from the operation.
A secondary reason might be simply to provide a complete abstraction. I.e. to not allow any of the underlying implementation to "leak" from the method, in the form of a Task
object that a caller might inspect or (worse) manipulate in a way that interferes with the correct and reliable operation of the task.
Why is it that the compute-bound methods can be implemented using
Task.Run()
, but not the I/O bound methods?
You can implement I/O bound operations using Task.Run()
, but why would you? Doing so commits a thread to the operation which, for an operation that would not otherwise require a thread, is wasteful.
I/O bound operations generally have support from an I/O completion port and the IOCP thread pool (the threads of which handle completions of an arbitrarily large number of IOCPs) and so it is more efficient to simply use the existing asynchronous I/O API, rather than to use Task.Run()
to call a synchronous I/O method.
Should I await a synchronous Task T that uses TaskCompletionSource?
Since you don't have a naturally-asynchronous API, I do not recommend using TaskCompletionSource<T>
. You can use the full CancellationToken
support with synchronous APIs, as such:
public Stuff GetStuff(CancellationToken token)
{
var stuff = new Stuff();
using (var stuffGetter = new StuffGetter())
{
stuffGetter.CancelCallback = () => token.IsCancellationRequested;
for (var x = 0; x < 10; x++)
{
token.ThrowIfCancellationRequested();
var thing = stuffGetter.GetSomething();
stuff.AddThing(thing);
}
return stuff;
}
}
When you write task-returning methods, it's important to follow the TAP guidelines. In this case, the naming convention means your AnalyzeStuff
should be called AnalyzeStuffAsync
.
Can you tell whether I need ConfigureAwait(false) somewhere? If so, how can you tell?
You should use ConfigureAwait(false)
unless you need the context in your method (the "context" is usually the UI context for client apps, or the request context for ASP.NET apps). You can find more information in my MSDN article on async
best practices.
So, assuming that StuffAnalyzer.Result
doesn't have any kind of UI thread dependency or anything like that, I'd write AnalyzeStuffAsync
as such:
public async Task<StuffAnalysis> AnalyzeStuffAsync(Stuff stuff, CancellationToken token)
{
var allTasks = new List<Task>();
using (var stuffAnalyzer = new StuffAnalyzer())
{
foreach (var thing in stuff)
{
allTasks.Add(stuffAnalyzer.AnalyzeThingAsync(thing));
}
await Task.WhenAll(allTasks).ConfigureAwait(false);
}
return stuffAnalyzer.Result;
}
Your GetAndAnalyzeStuffAsync
is a more complex situation, where you have both blocking and asynchronous code in the method. In this case, the best approach is to expose it as an asynchronous API but with a clear comment noting that it is blocking.
// <summary>Note: this method is not fully synchronous; it will block the calling thread.</summary>
public async Task<StuffAnalysis> GetAndAnalyzeStuffAsync(CancellationToken token)
{
var stuff = GetStuff(token);
var analysis = await AnalyzeStuff(stuff, token).ConfigureAwait(false);
return analysis;
}
which can simplify to:
// <summary>Note: this method is not fully synchronous; it will block the calling thread.</summary>
public Task<StuffAnalysis> GetAndAnalyzeStuffAsync(CancellationToken token)
{
var stuff = GetStuff(token);
return AnalyzeStuff(stuff, token);
}
How do I deal with the Task.Run() call in the calling code (Main() below)?
You are using it correctly. I describe this situation on my blog. In a Console application it's not common to use Task.Run
like this, but there's nothing wrong with doing it this way. Task.Run
is usually used to free up the UI thread in a UI application.
I assume Task.Run is more appropriate than Task.Factory.StartNew b/c I'm doing async stuff.
Yes, it is.
Related Topics
How to Access Backing Fields Behind Auto-Implemented Properties
Why Are Extension Methods Only Allowed in Non-Nested, Non-Generic Static Class
Why Does the Equals Implementation for Anonymous Types Compare Fields
Can Timers Get Automatically Garbage Collected
How to Compare Color Object and Get Closest Color in an Color[]
Show Properties of a Navigation Property in Datagridview (Second Level Properties)
How to Loop Through Items in a List Box and Then Remove Those Item
C# Webbrowser Control - Form Submit Not Working Using Invokemember("Click")
Implicit Operator Using Interfaces
How to Use the Paint Event to Draw Shapes at Mouse Coordinates
When Is It Appropriate to Use C# Partial Classes
When Should Taskcompletionsource<T> Be Used
C#: Printing All Properties of an Object
How to Parse a Month Name (String) to an Integer for Comparison in C#
How to Merge Multiple PDF Files (Generated in Run Time)
Remove HTML Tags from String Including &Nbsp in C#
Creating a Zip Archive in Memory Using System.Io.Compression