Should async method always call await, if not what is the implication? - c#

In this example,
private async void Button1_Click(object sender, EventArgs e)
{
if (condition) await Task.Run(Foo);
}
private void Foo()
{
Thread.Sleep(5000);
}
sometimes condition is false, and the async method awaits nothing. But consider this example,
private async void Button1_Click(object sender, EventArgs e)
{
await Task.Run(Foo);
}
private void Foo()
{
if (condition) Thread.Sleep(5000);
}
where the method is always awaited, even if condition is false. I'm wondering what happens behind the scenes, if one is more preferable to the other, I mean objectively if there are compiler optimizations which make one preferable over the other. Assume condition can be checked from any thread, and has a very minimal impact to performance.
It seems that when the condition check is deferred, there is always a task being awaited, but when it's false in the handler, I have a situation which is close to one where the async method lacks an await operator, about which the compiler warns me. So it feels both wrong and right at the same time. Hoping someone can shed some objective light on this question.

Should async method always call await, if not what is the implication?
As I describe on my blog, async methods always begin executing synchronously, just like any other methods. await is where things may get asynchronous.
If a code path is taken where there is no await (or if the tasks awaited are already completed), then the method completes synchronously and returns an already-completed task to its caller. This is not a problem in practice, because "*Async" means "may be asynchronous", not "must be asynchronous".
In this specific example, you're using async void, but if this was an async Task method that was called a lot, in that case I'd recommend considering returning ValueTask instead, which would save some memory allocation for the Task whenever it completes synchronously.

The async keyword on the method is mearly an indicator to the compiler that the code inside the method should be converted into a state-machine with state transitions at the awaits.
The only technical down-side, that I am aware of, to having an async method that doesn't await anything, is the small amount of overhead introduced by the state-machine. It should have no negative effects, other than a very small performance impact, and a tiny bit of memory-pressure, that can most likely be ignored in your scenario.

In your particular case Thread.Sleep(5000) is not the best way of pausing the execution. Thread.Sleep(5000) is an old API and it blocks the execution thread for a given time (5000ms). What does that mean? It means the thread won't be available for other tasks. Let's say, given your computer CPU output, you have 4 threads in total and you lock 1 of them for 5000ms - it is really bad especially if it is a web application that has to handle concurrent API requests. What to use instead? await Task.Delay(5000) it will "hang" the execution for a given time, yet the thread is going to come back to the thread pool and will be available for other tasks.
Now closer to your question. Wrapping non-asynchronous code into Task and awaiting won't do anything. Async/await was designed for I/O operations specifically for not blocking threads that are waiting for I/O to complete. Wrapping code into Task and not awaiting - is basically "fire and forget" which means your execution thread may continue to execute the next block without waiting for your Task method to complete. Why I say may is because if your code wrapper in Task is fast enough it would work synchronously

Related

Await/async on synchronous methods, what is an "asyncrhonous process"? When does "control return to the caller"? Nested awaits [duplicate]

This question already has answers here:
When does 'await' immediately leave a method and when does it not?
(6 answers)
Closed 9 months ago.
After reading a lot about async/await I still have a fundamental question (that I kind of assume the answer but I just want confirmation).
Let's discuss over some code:
async Task methodA() {
//code A (without awaits or Task.Run)
Task.Run(()=> //code A' (without awaits or Task.Run))
}
async Task methodB() {
//code B (without awaits or Task.Run)
await methodA();
}
void Main() {
//code C (without awaits or Task.Run)
methodB(); (no await)
//code D (without awaits or Task.Run, no pun intended)
}
What I think will happen:
Run code C
Run code B
Run code A
Run code A' and code D (different threads, in parallel)
The doubt comes from what's in the documentation:
The await operator suspends evaluation of the enclosing async method
until the asynchronous operation represented by its operand completes. [...]
The await operator doesn't block the thread that evaluates the async method.
The key being here what represents an asynchronous operation. Cause considering this other piece of documentation:
The await operator tells the compiler that the async method can't
continue past that point until the awaited asynchronous process is
complete. In the meantime, control returns to the caller of the async
method.
One might think that, since methodA() is async, once methodB reaches the await methodA(), then it might return control to the caller and allow to run code D in parallel to code A and code A'.
The essence is if method A constitutes an asynchronous operation as described in the documentation, or only until it reaches truly async code (code that runs on another thread) then it actually allows control to return to the caller (which in this case is code A').
My understanding is that asynchronous operation is an operation that will run on another thread, is this correct? So things like Task.Run, Task.Delay, Task.Sleep..? And that even though code A is within an async method, it is not an async operation and control does not go immediately back when awaiting methodA. Is this correct?
Just for completion, check this question where in the answer's comments they argue precisely about this, without a very clear answer.
[...], control returns to the caller of the async method.
This sentence had confused the hell out of me when I was learning asynchronous programming. What it means is that the Task is created and returned. You can think the asynchronous methods as generators for Task objects. The async method has to do some work in order to create the Task. This work completes when the first await of an incomplete awaitable inside the method is reached. At that point the Task is created, it is handed to the caller of the method, and the caller can do whatever it wants with it.
Usually the caller awaits the task, but it can also do other things before awaiting it, or it might never await it and completely ignore it, although this is not recommended. Generally you want to keep track of your tasks, and not let them run unattended in a fire-and-forget fashion.
When the caller does not await immediately the returned task, and the task was not in a completed state upon creation, you introduce concurrency in your application: More than one things might be happening at overlapping time spans. You can read about the differences between concurrency and parallelism here.

Using await in same line as method call c#

Can calling await in the same line as the async method be slower than calling normal method?
From what I know asynchronous methods are good for I/O operations like getting data from the database. But what if there is nothing to do between calling the async method and awaiting it I need to do it in the same line.
In this post Await and Async in the same line they discussed that the benefit comes from freeing thread.
I have some unit tests for testing my services and methods using async methods as I described are always taking longer than their non-async equivalents. I assume it's because creating work in a separate thread and then awaiting it has some price.
So what I want to ask is, if using async in this case has truly some benefits.
public async Task AssignHighestRank(User user)
{
user.Rank = await _rankRepository.GetHighestRank();
_userRepository.Update(user);
await _userRepository.SaveChanges();
}
async implementation uses additional CPU cycles, so in this sense an async method would be slightly slower than its equivalent that is not asynchronous. However, using such method together with other async methods may improve performance.
For example, consider a situation when you need to make multiple changes at once:
public async Task RecordBattleResultAsync(User winner, User loser) {
await Task.WhenAll(
AssignHighestRankAsync(winner)
, AssignLowestRankAsync(loser)
).ConfigureAwait(false);
}
This method would exploit the fact that both your methods are async for a potential speed-up.
Based on this investigation async method works slowly with consistently operations then regular one does the same (if we do not regard that async methods does not hold execution thread unlike regular one) :
Should I worry about "This async method lacks 'await' operators and will run synchronously" warning
due to huge amount of compiler's works under the hood. But using operation Task.WhenAll (creation task that is completed when all task are completed too - main thread is not blocked) and Task.WaitAll (almost the same save for main thread is blocked) with independence data task might increase speed-up of method execution (methods but not whole application in case Task.WaitAll) because of parallel task execution .

Are non-thread-safe functions async safe?

Consider the following async function that modifies a non-thread-safe list:
async Task AddNewToList(List<Item> list)
{
// Suppose load takes a few seconds
Item item = await LoadNextItem();
list.Add(item);
}
Simply put: Is this safe?
My concern is that one may invoke the async method, and then while it's loading (either on another thread, or as an I/O operation), the caller may modify the list.
Suppose that the caller is partway through the execution of list.Clear(), for example, and suddenly the Load method finishes! What will happen?
Will the task immediately interrupt and run the list.Add(item); code? Or will it wait until the main thread is done with all scheduled CPU tasks (ie: wait for Clear() to finish), before running the code?
Edit: Since I've basically answered this for myself below, here's a bonus question: Why? Why does it immediately interrupt instead of waiting for CPU bound operations to complete? It seems counter-intuitive to not queue itself up, which would be completely safe.
Edit: Here's a different example I tested myself. The comments indicate the order of execution. I am disappointed!
TaskCompletionSource<bool> source;
private async void buttonPrime_click(object sender, EventArgs e)
{
source = new TaskCompletionSource<bool>(); // 1
await source.Task; // 2
source = null; // 4
}
private void buttonEnd_click(object sender, EventArgs e)
{
source.SetResult(true); // 3
MessageBox.Show(source.ToString()); // 5 and exception is thrown
}
No, its not safe. However also consider that the caller might also have spawned a thread and passed the List to its child thread before calling your code, even in a non async environment, which will have the same detrimental effect.
So; although not safe, there is nothing inherently thread-safe about receiving a List from a caller anyway - there is no way of knowing whether the list is actually being processed from other threads that your own.
Short answer
You always need to be careful using async.
Longer answer
It depends on your SynchronizationContext and TaskScheduler, and what you mean by "safe."
When your code awaits something, it creates a continuation and wraps it in a task, which is then posted to the current SynchronizationContext's TaskScheduler. The context will then determine when and where the continuation will run. The default scheduler simply uses the thread pool, but different types of applications can extend the scheduler and provide more sophisticated synchronization logic.
If you are writing an application that has no SynchronizationContext (for example, a console application, or anything in .NET core), the continuation is simply put on the thread pool, and could execute in parallel with your main thread. In this case you must use lock or synchronized objects such as ConcurrentDictionary<> instead of Dictionary<>, for anything other than local references or references that are closed with the task.
If you are writing a WinForms application, the continuations are put in the message queue, and will all execute on the main thread. This makes it safe to use non-synchronized objects. However, there are other worries, such as deadlocks. And of course if you spawn any threads, you must make sure they use lock or Concurrent objects, and any UI invocations must be marshaled back to the UI thread. Also, if you are nutty enough to write a WinForms application with more than one message pump (this is highly unusual) you'd need to worry about synchronizing any common variables.
If you are writing an ASP.NET application, the SynchronizationContext will ensure that, for a given request, no two threads are executing at the same time. Your continuation might run on a different thread (due to a performance feature known as thread agility), but they will always have the same SynchronizationContext and you are guaranteed that no two threads will access your variables at the same time (assuming, of course, they are not static, in which case they span across HTTP requests and must be synchronized). In addition, the pipeline will block parallel requests for the same session so that they execute in series, so your session state is also protected from threading concerns. However you still need to worry about deadlocks.
And of course you can write your own SynchronizationContext and assign it to your threads, meaning that you specify your own synchronization rules that will be used with async.
See also How do yield and await implement flow of control in .NET?
Assuming the "invalid acces" occures in LoadNextItem(): The Task will throw an exception. Since the context is captured it will pass on to the callers thread so list.Add will not be reached.
So, no it's not thread-safe.
Yes I think that could be a problem.
I would return item and add to the list on the main tread.
private async void GetIntButton(object sender, RoutedEventArgs e)
{
List<int> Ints = new List<int>();
Ints.Add(await GetInt());
}
private async Task<int> GetInt()
{
await Task.Delay(100);
return 1;
}
But you have to call from and async so I do not this this would work either.

Async method not running in parallel

In the following code, in the B method, the code Trace.TraceInformation("B - Started"); never gets called.
Should the method be running in parallel?
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
private static async Task A()
{
for (;;)
{
}
}
private static async Task B()
{
Trace.TraceInformation("B - Started");
}
static void Main(string[] args)
{
var tasks = new List<Task> { A(), B() };
Task.WaitAll(tasks.ToArray());
}
}
}
Short answer
No, as you wrote your two async methods, they are indeed not running in parallel. Adding await Task.Yield(); to your first method (e.g. inside the loop) would allow them to do so, but there are more reasonable and straightforward methods, highly depending on what you actually need (interleaved execution on a single thread? Actual parallel execution on multiple threads?).
Long answer
First of all, declaring functions as async does not inherently make them run asynchronously or something. It rather simplifies the syntax to do so - read more about the concepts here: Asynchronous Programming with Async and Await
Effectively A is not asynchronous at all, as there is not a single await inside its method body. Instructions up to the first use of await run synchronously like a regular method would.
From then on, the object that you await determines what happens next, i.e. the context that the remaining method runs in.
To force execution of a task to happen on another thread, use Task.Run or similar.
In this scenario, adding await Task.Yield() does the trick since the current synchronization context is null and this happens to indeed cause the task scheduler (should be ThreadPoolTaskScheduler) to execute the remaining instuctions on a thread-pool thread - some environment or configuration might cause you to only have one of them, so things would still not run in parallel.
Summary
The moral of the story is: Be aware of the differences between two concepts:
concurrency (which is enabled by using async/await reasonably) and
parallelism (which only happens when concurrent tasks get scheduled the right way or if you enforce it using Task.Run, Thread, etc. in which case the use of async is completely irrelevant anyway)
The async modifier is not a magic spawn-a-thread-here marker. It's sole purpose is to let the compiler know a method might depend on some asynchronous operation (A complex data-processing thread, I/O...) so it has to setup a state machine to coordinate the callbacks resulting from those asynchronous operations.
To make A run on another thread you would invoke it using Task.Run which wraps the invocation on a new thread with a Task object, which you can await. Be aware that await-ing a method does not mean your code runs in parallel to A's execution all by itself: It will until the very line you await the Task object, telling the compiler you need the value that the Task object returns. In this case await-ing Task.Run(A) will effectively make your program run forever, waiting for A to return, something that will never happen (barring computer malfunction).
Do have in mind that marking a method as async but not actually awaiting anything will only have the effect of a compiler warning. If you await something that is not truly async (It returns immediately on the calling thread with something like Task.FromResult) it will mean your program takes a runtime speed penalty. It is very slight, however.
No, the methods shown are not expected to "run in parallel".
Why B is never called - you have list of tasks tasks constructed via essentially series of .Add calls - and first is result of A() is added. Since the A method does not have any await it will run to the completion synchronously on the same thread. And after that B() would be called.
Now A will never complete (it is sitting in infinite loop) so really code will not even reach call to B.
Note that even if creation would succeed code never finish WaitAll as A still sits in infinite loop.
If you want methods to "run in parallel" you need to either run them implicitly/explicitly on new threads (i.e. with Task.Run or Thread.Start) or for I/O bound calls let method to release thread with await.

When to run an async function concurrently?

The async-await features make it elegant to write non-blocking code. But, while non blocking, the work performed within an async function can still be non-trivial.
When writing async code, I find it natural to write code that follows the pattern 'all the way down the rabbit hole', so to speak, where all methods within the calling tree are marked async and the APIs used are async; but even while non blocking, the executed code can take up a fair amount of the contextual thread's time.
How and when do you decide to run an async-able method concurrently on top of asynchronously? Should one err on having the new Task created higher or lower in the call tree? Are there any best practices for this type of 'optimization'?
I've been using async in production for a couple of years. There are a few core "best practices" that I recommend:
Don't block on async code. Use async "all the way down". (Corollary: prefer async Task to async void unless you have to use async void).
Use ConfigureAwait(false) wherever possible in your "library" methods.
You've already figured out the "async all the way down" part, and you're at the point that ConfigureAwait(false) becomes useful.
Say you have an async method A that calls another async method B. A updates the UI with the results of B, but B doesn't depend on the UI. So we have:
async Task A()
{
var result = await B();
myUIElement.Text = result;
}
async Task<string> B()
{
var rawString = await SomeOtherStuff();
var result = DoProcessingOnRawString(rawString);
return result;
}
In this example, I would call B a "library" method since it doesn't really need to run in the UI context. Right now, B does run in the UI thread, so DoProcessingOnRawString is causing responsiveness issues.
So, add a ConfigureAwait(false) to every await in B:
async Task<string> B()
{
var rawString = await SomeOtherStuff().ConfigureAwait(false);
var result = DoProcessingOnRawString(rawString);
return result;
}
Now, when B resumes after awaiting SomeOtherStuff (assuming it did actually have to await), it will resume on a thread pool thread instead of the UI context. When B completes, even though it's running on the thread pool, A will resume on the UI context.
You can't add ConfigureAwait(false) to A because A depends on the UI context.
You also have the option of explicitly queueing tasks to the thread pool (await Task.Run(..)), and you should do this if you have particular CPU-intensive functionality. But if your performance is suffering from "thousands of paper cuts", you can use ConfigureAwait(false) to offload a lot of the async "housekeeping" onto the thread pool.
You may find my intro post helpful (it goes into more of the "why's"), and the async FAQ also has lots of great references.
Async-await does not actually use threads in the current .NET process-space. it is designed for "blocking" IO and network operations, like database calls, web requests, some file IO.
I cannot perceive what advantage there would be in C# to what you call the rabbit-hole technique. Doing so only obscures the code and unnecessarily couples your potentially high-cpu code to your IO code.
To answer your question directly, I would only use async-await for the aforementioned IO/network scenarios, right at the point where you are doing the blocking operations, and for anything that was CPU bound I would use threading techniques to make the best use of the available CPU cores. No need to mix the two concerns.

Categories

Resources