I'm currently reading "Concurrency in C# Cookbook" by Stephen Cleary, and I noticed the following technique:
var completedTask = await Task.WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
downloadTask is a call to httpclient.GetStringAsync, and timeoutTask is executing Task.Delay.
In the event that it didn't timeout, then downloadTask is already completed. Why is necessary to do a second await instead of returning downloadTask.Result, given that the task is already completed?
There are already some good answers/comments here, but just to chime in...
There are two reasons why I prefer await over Result (or Wait). The first is that the error handling is different; await does not wrap the exception in an AggregateException. Ideally, asynchronous code should never have to deal with AggregateException at all, unless it specifically wants to.
The second reason is a little more subtle. As I describe on my blog (and in the book), Result/Wait can cause deadlocks, and can cause even more subtle deadlocks when used in an async method. So, when I'm reading through code and I see a Result or Wait, that's an immediate warning flag. The Result/Wait is only correct if you're absolutely sure that the task is already completed. Not only is this hard to see at a glance (in real-world code), but it's also more brittle to code changes.
That's not to say that Result/Wait should never be used. I follow these guidelines in my own code:
Asynchronous code in an application can only use await.
Asynchronous utility code (in a library) can occasionally use Result/Wait if the code really calls for it. Such usage should probably have comments.
Parallel task code can use Result and Wait.
Note that (1) is by far the common case, hence my tendency to use await everywhere and treat the other cases as exceptions to the general rule.
This makes sense if timeoutTask is a product of Task.Delay, which I believe what it is in the book.
Task.WhenAny returns Task<Task>, where the inner task is one of those you passed as arguments. It could be re-written like this:
Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)
return null;
return downloadTask.Result;
In either case, because downloadTask has already completed, there's a very minor difference between return await downloadTask and return downloadTask.Result. It's in that the latter will throw AggregateException which wraps any original exception, as pointed out by #KirillShlenskiy in the comments. The former would just re-throw the original exception.
In either case, wherever you handle exceptions, you should check for AggregateException and its inner exceptions anyway, to get to the cause of the error.
Related
Section 10.2 within Concurrency in .NET provides the following listing for an Otherwise task combinator:
public static Task<T> Otherwise<T>(this Task<T> task, Func<Task<T>> orTask) =>
task.ContinueWith(async innerTask =>
{
if (innerTask.Status == TaskStatus.Faulted) return await orTask();
return await Task.FromResult<T>(innerTask.Result);
}).Unwrap();
(Confusingly, within the printed book, Otherwise is marked as async which fails to compile; however, not in the GH listing which compiles without issue)
My "understanding" is that if the anonymous continuation is called, it would suggest that the supplied innerTask has completed by that point, successfully or otherwise.
Assuming successful, it would also suggest that innerTask.Result would already be determined with no synchronous waiting required upon calling that member property.
On the mild assumption that above is sensible... What possible benefit is there from using return await Task.FromResult<T>(innerTask.Result) as opposed to just return innerTask.Result? (the latter of which also compiles).
Unless I'm missing something (quite likely)... It feels overly verbose.
Update:
Thank you to everyone who has contributed; I never anticipated this would get as much feedback as it has!
At one point, I was concerned about a situation where innerTask.Result could raise an exception and how that would be dealt with by the 2x return approaches above.
From my own testing since, I can see that if innerTask was cancelled and, hence innerTask.Status == TaskStatus.Cancelled, the combinator implementation as given would permit the accessing of innerTask.Result which would then raise an AggregateException.
However, there is no difference in how this (or any other exception I imagine) would be treated by either return approach from within an async function.
Given this, and the information provided, it appears as though the former approach offers nothing over and above the latter. If anything, it (IMHO) obscurs the intention and has sent me down a (albeit illuminating) rabbit hole.
What possible benefit is there from using return await Task.FromResult<T>(innerTask.Result) as opposed to just return innerTask.Result?
Absolutely zero benefit. That's the kind of code that your end up writing when you are struggling with a concept that you don't fully understand, and you reach to a version that compiles and works correctly. Your code might end up decorated with some useless cargo, which nonetheless is not harmful, so it might survive multiple code reviews and revisions.
Here is a better way to write the Otherwise extension method:
public static Task<T> Otherwise<T>(this Task<T> task, Func<Task<T>> onErrorTaskFactory)
{
return task.ContinueWith(innerTask =>
{
if (innerTask.IsFaulted) return onErrorTaskFactory();
return innerTask;
}, default, TaskContinuationOptions.DenyChildAttach |
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default).Unwrap();
}
Notice that the continuation is not async, and it returns the innerTask, not its result. With this version you get these benefits:
In case the onErrorTaskFactory() returns a Task that also fails, and it contains more that one exceptions (think of passing a lambda that return Task.WhenAll()), all the errors will be propagated, not just the first one.
In case the innerTask is canceled, the cancellation will be propagated without an additional internal try/catch. Throwing and catching exceptions in C# is quite expensive.
The code complies with the CA2008 guideline ("Do not create tasks without passing a TaskScheduler").
The onErrorTaskFactory will be invoked synchronously on the thread that completed the innerTask, avoiding a thread-switch.
These benefits are not ground-breaking. I would evaluate the original implementation in the book as quite decent. It doesn't have any terrible bugs or inefficiencies.
For some heavy caching / high performance path in my project I need to "cast" a Task<X> to a Task<Y> type - see here.
I used the ContinueWith solution, but I noticed that even when my original task is in a RanToCompletion state, the ContinueWith still returns a Task that is WaitingToRun.
var task = Task.FromResult("cheese");
var test = task.ContinueWith(x => (object)x);
Console.WriteLine(test.Status); // WaitingToRun
Console.ReadLine();
For reasons out of the scope of this question, this gives me some extra cost further down the line.
If I do the following, my very specific problem seems to be fixed and it will directly return a CompletedTask, otherwise it does the normal logic.
if ( task.IsCompletedSuccessfully )
return Task.FromResult((object)task.Result);
else
return task.ContinueWith(x => (object)x);
Is it safe to add this "happy path" to my logic?
Because this path is not in the normal ContinueWith logic I am afraid I am missing something and this will bite me when running in production in a high concurrent environment.
As is the case just about anytime ContinueWith comes up, the answer is to use await instead. Using ContinueWith correctly is really hard, and it's almost never the most effective solution to any given problem.
You've found one problem with ContinueWith, namely that without being really careful with your scheduler (or manually checking if the task is completed) you've ended up forcing work to be scheduled into a thread pool thread to run later when it just doesn't need to be. (That won't always happen, so you can't rely on that behavior, but it's possible, which is frankly, worse.)
Additionally, your ContinueWith code doesn't handle errors/cancellation properly. In your code cancelled tasks become errored instead, and errored tasks have their exception wrapped in an AggregateException.
Your code also has a bug in that you don't get the result from the task before casting it, and thus the result of your task is another task cast to an object, but that part is easily fixed by just adding in a Result call.
Here is a pre-await solution attempting to solve these problems, but in a post-await world, there's just no need.
public static async Task<TResult> Cast<TSource, TResult>(
this Task<TSource> task)
{
return (TResult)(object) await task.ConfigureAwait(false);
}
Not only is await a more effective syntax for scheduling continuations, but it's default behavior is much more likely to be correct in any given situation than the default behaviors of ContinueWith. It'll check if the task is completed and only schedule a continuation to run later if it's not, it doesn't wrap exceptions in an AggregateException, etc. The only default behavior that we don't want here is that by default await maintains the current synchronization context, which is often correct, but happens to not be needed for our method here.
public async Task GetLiveCandleStick(string param)
{
await Clients.All.SendAsync("ReceiveMessage", param);
}
Please consider the above code.
Almost all of the tutorial await Clients.xxx.SendAsync methods. However, I find that it works fine even I take away the await keyword. Then what is the point of using await? (I have seen a thread explaned that await is not meant to "wait until client receive the message...")
Thanks in advance.
By omitting the await keyword, the method would become fire-and-forget; this is almost never the right thing to do, as you would have no way of knowing that the Task had completed.
Another issue is exception handling. If an exception occurs when you call SendAsync, it is placed on the Task rather than being thrown directly, and would simply be garbage collected without using await.
Having said this, if your GetLiveCandleStick method is a simple wrapper as you suggest, it would be slightly more efficient to simply return the Task from SendAsync rather than create a new one using async:
public Task GetLiveCandleStick(string param)
{
return Clients.All.SendAsync("ReceiveMessage", param);
}
In this case only, you don't need to use await - nothing else is happening after SendAsync so there's no need to await. Awaiting on GetLiveCandleStick would behave the same as awaiting on SendAsync. You could convert this method to :
public Task GetLiveCandleStick(string param)=> Clients.All.SendAsync("ReceiveMessage", param);
Tutorials use await because quite often there is code after SendAsync, that should be executed after SignalR enqueues the message.
You may want to add error handling in case SignalR throws for example, or ensure a Begin message is enqueued after an End message in a tight loop. Having an await even for a single statement method can be useful for debugging too.
Finally, good tutorials shouldn't introduce unrelated concepts as this can easily lead to confusion. A SignalR tutorial isn't the best place for explaining the await state machine, or when await can be skipped. Unfortunately, too many examples in learn.microsoft.com try to put everything into a single page, resulting in quite a bit of confusion.
The keyword "await" is for async methods. It's smarter than "wait", it's async wait!
This means that wait the return of message during exec the code until the result is necessary used. So You don't have to use Thread.Wait in a line of code, but the compiler place the wait in the right position. In this case the method that call the await is async, so the wait of Clients.All.SendAsync is out of function.
I'm currently reading "Concurrency in C# Cookbook" by Stephen Cleary, and I noticed the following technique:
var completedTask = await Task.WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
downloadTask is a call to httpclient.GetStringAsync, and timeoutTask is executing Task.Delay.
In the event that it didn't timeout, then downloadTask is already completed. Why is necessary to do a second await instead of returning downloadTask.Result, given that the task is already completed?
There are already some good answers/comments here, but just to chime in...
There are two reasons why I prefer await over Result (or Wait). The first is that the error handling is different; await does not wrap the exception in an AggregateException. Ideally, asynchronous code should never have to deal with AggregateException at all, unless it specifically wants to.
The second reason is a little more subtle. As I describe on my blog (and in the book), Result/Wait can cause deadlocks, and can cause even more subtle deadlocks when used in an async method. So, when I'm reading through code and I see a Result or Wait, that's an immediate warning flag. The Result/Wait is only correct if you're absolutely sure that the task is already completed. Not only is this hard to see at a glance (in real-world code), but it's also more brittle to code changes.
That's not to say that Result/Wait should never be used. I follow these guidelines in my own code:
Asynchronous code in an application can only use await.
Asynchronous utility code (in a library) can occasionally use Result/Wait if the code really calls for it. Such usage should probably have comments.
Parallel task code can use Result and Wait.
Note that (1) is by far the common case, hence my tendency to use await everywhere and treat the other cases as exceptions to the general rule.
This makes sense if timeoutTask is a product of Task.Delay, which I believe what it is in the book.
Task.WhenAny returns Task<Task>, where the inner task is one of those you passed as arguments. It could be re-written like this:
Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)
return null;
return downloadTask.Result;
In either case, because downloadTask has already completed, there's a very minor difference between return await downloadTask and return downloadTask.Result. It's in that the latter will throw AggregateException which wraps any original exception, as pointed out by #KirillShlenskiy in the comments. The former would just re-throw the original exception.
In either case, wherever you handle exceptions, you should check for AggregateException and its inner exceptions anyway, to get to the cause of the error.
A few posts on stack overflow have suggested the following:
Any async method where you have a single await expression awaiting a Task or Task < T >, right at the end of the method with no further processing, would be better off being written without using async/await.
Architecture for async/await
Await or Return
Is this advice just for particular circumstances? On a web server, isn't one of the main reasons to use async/await, being that while awaiting something like the UpdateDataAsync method below, the Thread will return to the ThreadPool, allowing the server to work on other requests?
Should SaveDataAsync await that DB update call given it's the last call in the method?
public async Task WorkWithDataAsync(Data data)
{
ManipulateData(data);
await SaveDataAsync(data);
await SendDataSomewhereAsync(data);
}
public async Task SaveDataAsync(Data data)
{
FixData(data);
await DBConnection.UpdateDataAsync(data);
}
Also, given you don't know where SaveDataAsync will be used, making it synchronous would hurt a method like WorkWithDataAsync would it not?
Removing the await and just returning the Task doesn't make the method synchronous. await is a tool that makes it easier to make a method asynchronous. It's not the only way to make a method asynchronous. It's there because it allows you to add continuations to task much more easily than you could without it, but in the method that you've shown you're not actually leveraging the await keyword to accomplish anything and as such you can remove it and have the code simply function identically.
(Note that technically the semantics of exceptions will have been slightly changed by removing async; thrown exceptions will be thrown from the method, not wrapped in the returned Task, if it matters to you. As far as any caller is concerned, this is the only observable difference.)
Writing these methods without the async-await keywords doesn't make them synchronous.
The idea is to return the task directly instead of having the overhead of generating the state machine.
The actual difference is about exception handling. An async method encapsulates exceptions inside the returned task while a simple task returning method doesn't.
If for example FixData throws an exception it will be captured in an async method but directly thrown in a simple task returning one. Both options would be equally asynchronous.