multiple awaits vs Task.WaitAll - equivalent? - c#

In terms of performance, will these 2 methods run GetAllWidgets() and GetAllFoos() in parallel?
Is there any reason to use one over the other? There seems to be a lot happening behind the scenes with the compiler so I don't find it clear.
============= MethodA: Using multiple awaits ======================
public async Task<IHttpActionResult> MethodA()
{
var customer = new Customer();
customer.Widgets = await _widgetService.GetAllWidgets();
customer.Foos = await _fooService.GetAllFoos();
return Ok(customer);
}
=============== MethodB: Using Task.WaitAll =====================
public async Task<IHttpActionResult> MethodB()
{
var customer = new Customer();
var getAllWidgetsTask = _widgetService.GetAllWidgets();
var getAllFoosTask = _fooService.GetAllFos();
Task.WaitAll(new List[] {getAllWidgetsTask, getAllFoosTask});
customer.Widgets = getAllWidgetsTask.Result;
customer.Foos = getAllFoosTask.Result;
return Ok(customer);
}
=====================================

The first option will not execute the two operations concurrently. It will execute the first and await its completion, and only then the second.
The second option will execute both concurrently but will wait for them synchronously (i.e. while blocking a thread).
You shouldn't use both options since the first completes slower than the second and the second blocks a thread without need.
You should wait for both operations asynchronously with Task.WhenAll:
public async Task<IHttpActionResult> MethodB()
{
var customer = new Customer();
var getAllWidgetsTask = _widgetService.GetAllWidgets();
var getAllFoosTask = _fooService.GetAllFos();
await Task.WhenAll(getAllWidgetsTask, getAllFoosTask);
customer.Widgets = await getAllWidgetsTask;
customer.Foos = await getAllFoosTask;
return Ok(customer);
}
Note that after Task.WhenAll completed both tasks already completed so awaiting them completes immediately.

Short answer: No.
Task.WaitAll is blocking, await returns the task as soon as it is encountered and registers the remaining part of the function and continuation.
The "bulk" waiting method you were looking for is Task.WhenAll that actually creates a new Task that finishes when all tasks that were handed to the function are done.
Like so: await Task.WhenAll({getAllWidgetsTask, getAllFoosTask});
That is for the blocking matter.
Also your first function does not execute both functions parallel. To get this working with await you'd have to write something like this:
var widgetsTask = _widgetService.GetAllWidgets();
var foosTask = _fooService.GetAllWidgets();
customer.Widgets = await widgetsTask;
customer.Foos = await foosTask;
This will make the first example to act very similar to the Task.WhenAll method.

As an addition to what #i3arnon said. You will see that when you use await you are forced to have to declare the enclosing method as async, but with waitAll you don't. That should tell you that there is more to it than what the main answer says. Here it is:
WaitAll will block until the given tasks finish, it does not pass control back to the caller while those tasks are running. Also as mentioned, the tasks are run asynchronous to themselves, not to the caller.
Await will not block the caller thread, it will however suspend the execution of the code below it, but while the task is running, control is returned back to the caller. For the fact that control is returned back to the caller (the called method is running async), you have to mark the method as async.
Hopefully the difference is clear. Cheers

Only your second option will run them in parallel. Your first will wait on each call in sequence.

As soon as you invoke the async method it will start executing. Whether it will execute on the current thread (and thus run synchronously) or it will run async is not possible to determine.
Thus, in your first example the first method will start doing work, but then you artificially stops the flow of the code with the await. And thus the second method will not be invoked before the first is done executing.
The second example invokes both methods without stopping the flow with an await. Thus they will potentially run in parallel if the methods are asynchronous.

Related

How does async await work when if there are multiple awaits in a method which aren't dependent on eachother?

Say I'm using EF and I query the database for multiple objects:
var person = await Context.People.FindAsync(1);
person.Name = "foo";
var animal = await Context.Animals.FindAsync(2);
animal.Name = "bar";
var hybrid = await Context.Hybrids.FindAsync(3);
hybrid.Name = "godless abomination";
I can see two ways this could execute:
We get stuck at the first await, it gets a person and sets its name, then moves on to the next await (and so on)
The compiler knows that each awaited task can be executed at the same time because they don't depend on each other. So it tries to get person, animal, and hybrid at the same time and then sets the person, animal and hybrid names synchronously.
I assume number 2 is correct, but I'm not sure. Advice for working these sorts of questions out for myself is also very welcome :)
async and await work with worker pool threads to improve responsiveness of code, not parallelism. In a thread-safe implementation you can parallelize multiple calls using Task.WhenAll instead of await, however the big caveat here is that the EF DbContext is not thread safe and will throw when it detects simultaneous access between multiple threads. (It doesn't matter if the tables referenced are related or not)
As a general breakdown of what sort-of happens with awaited async code:
var person = await Context.People.FindAsync(1);
person.Name = "foo";
var animal = await Context.Animals.FindAsync(2);
animal.Name = "bar";
Context.People.FindAsync(1) gets set up to be executed on a worker thread which will be given an execution pointer for a resumption point to resume executing all following code once it is completed since it was awaited.
So a request like this comes in on a Web Request worker thread, which gets to an awaited line, requests a new worker thread to take over, giving it a resumption pointer to the remaining code, then since the call is awaited, the Web Request worker thread knocks off back into the pool to serve more requests while that runs in the background.
When the Context.People.FindAsync() call completes, that worker thread continues the execution, and eventually hits the async Context.Animals.FindAsync(2) call which does the same thing, spawning that call off to a worker thread with a resumption point given it's awaited, and knocks off back into the worker thread pool. The actual thread handoff behaviour can vary depending on the configured synchronization context. (which can be set up to have the resumption code return to the original thread, for example the UI thread in a WinForms project)
The EF DbContext is fine with this since operations against it are only occurring from one thread at any given time. The accidental alternative would be something like this:
var person = Context.People.FindAsync(1);
var animal = Context.Animals.FindAsync(2);
person.Result.Name = "foo";
animal.Result.Name = "bar";
Context.People.FindAsync(1) will spawn off and run on a worker thread, but because it is not awaited, it isn't given an resumption point to call when it completes, it just returns the Task<Person> handle that initiates to represent its execution. This means that the calling thread continues with the Context.Animals.FindAsync(2) call, again handing off to a Worker Thread and getting a Task<Animal> back. Both of these worker threads will be running in parallel and the EF DbContext is not going to like that at all. Each Task will block on the .Result reference to wait for the task to complete, but often async calls are used where we don't care about the result which leads to silent errors creeping in. For instance some times there are multiple async calls with a bit of synchronous work happening in-between that always seems to take long enough that an un-awaited async call never seems to overlap... until one day it does in production.
Where you would want parallel execution you would opt instead for something like a WhenAll():
using(var contextOne = new AppDbContext());
using(var contextTwo = new AppDbContext());
var personTask = contextOne.People.FindAsync(1);
var animalTask = contextTwo.Animals.FindAsync(2);
await Task.WhenAll(personTask, animalTask);
person.Result.Name = "foo";
animal.Result.Name = "bar";
contextOne.SaveChanges();
contextTwo.SaveChanges();
The key differences here is that we need to ensure that the queries are run against different DbContext instances. Something like this can be used to parallelize DB operations with other long-running tasks (file management, etc.) When it comes to handling exceptions this would bubble up the first exception (if any was raised) from any of the awaited WhenAll tasks. If you want access potential exceptions from all parallel tasks, (AggregateException) that involves a bit of work to play nice with async/await without blocking.
see: Why doesn't await on Task.WhenAll throw an AggregateException? if you want to dive deeper down that rabbit hole. :)
Answer
Your code is going to stop at each await until the specific asynchronous function it proceeds returns. This means your code is going to run from top to bottom (i.e. number 1 is correct)
Example
await longAsyncFunc()
shortFunc()
Above, shortFunc() must wait until the longAsyncFunc() returns because of the await keyword.
Below, longAsyncFunc() will start execution, then shortFunc() will start execution -- no need to wait for longAsyncFunc() to finish its computation and return.
longAsyncFunc()
shortFunc()
Suggestion
If you would rather your code work like number 2 then I would wrap each pair of statements in an async function.
async funcA() {
var person = await Context.People.FindAsync(1);
person.Name = "foo";
return person;
}
async funcB() {
var animal = await Context.Animals.FindAsync(2);
animal.Name = "bar";
return animal;
}
async funcC() {
var hybrid = await Context.Hybrids.FindAsync(3);
hybrid.Name = "godless abomination";
return hybrid;
}
var person = funcA();
var animal = funcB();
var hybrid = funcC();
Notice that I didn't use await on the last three lines -- that would change the behavior back to number 1.

What does accessing a Result on a Task before calling Wait actually do?

var task = Task.Run(() => DoSomeStuff()).Result;
What happens here under the hood?
I made a tiny test:
using System;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
var r = Task.Run( () => {Thread.Sleep(5000); return 123; }).Result;
Console.WriteLine(r);
}
}
It prints "123" after 5s. So does accessing any such property on Task act as a shortcut to calling Task.Wait() i.e. is this safe to do?
Previously my code called Task.Delay(5000) which returned "123" immediately. I fixed this in my question but leave this here as comments and answers reference it.
You asked two questions. First, does accessing Result implicitly cause a synchronous Wait? Yes. But the more important question is:
is this safe to do?
It is not safe to do this.
It is very easy to get into a situation where the task that you are synchronously waiting on has scheduled work to run in the future before it completes onto the thread that you just put to sleep. Now we have a situation where the thread will not wake up until the sleeping thread does some work, which it never does because it is asleep.
If you already know that the task is complete then it is safe to synchronously wait for the result. If you do not, then it is not safe to synchronously wait.
Now, you might say, suppose I know through some other means that it is safe to wait synchronously for an incomplete task. Then is it safe to wait? Well, by the assumption of the question, yes, but it still might not be smart to wait. Remember, the whole point of asynchrony is to manage resources efficiently in a world of high latency. If you are synchronously waiting for an asynchronous task to complete then you are forcing a worker to sleep until another worker is done; the sleeping worker could be doing work! The whole point of asynchrony is to avoid situations where workers go idle, so don't force them to.
An await is an asynchronous wait. It is a wait that means "wait on running the rest of the current workflow until after this task is done, but find something to do while you are waiting". We did a lot of work to add it to the language, so use it!
So does accessing any such property on Task act as a shortcut to calling Task.Wait()?
Yes.
From the docs:
Accessing the [Result] property's get accessor blocks the calling thread until the asynchronous operation is complete; it is equivalent to calling the Wait method.
However, your test doesn't do what you think it does.
Task.Delay(..) returns a Task which completes after the specified amount of time. It doesn't block the calling thread.
So () => { Task.Delay(5000); return 123; } simply creates a new Task (which will complete in 5 seconds), then throws it away and immediately returns 123.
You can either:
Block the calling thread, by doing Task.Delay(5000).Wait() (which does the same thing as Thread.Sleep(5000))
Asynchronously wait for the Task returned from Task.Delay to complete: Task.Run(async () => { await Task.Delay(5000); return 123; })
The test doesn't wait for Task.Delay() so it returns immediatelly. It should be :
var r = Task.Run(async () => { await Task.Delay(5000); return 123; }).Result;
The behavior of Result is well defined - if the task hasn't completed, it blocks until it does. Accessing other Task properties doesn't block

Why do we need more than one `await` statement in a C# method?

Why do we need more than one await statement in a C# method?
E.g. here we have three await statements:
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore
{
public class BookStoreDataSeederContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Book, Guid> _bookRepository;
public BookStoreDataSeederContributor(IRepository<Book, Guid> bookRepository)
{
_bookRepository = bookRepository;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() > 0)
{
return;
}
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
}
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
}
);
}
}
}
In case we will remove the second and the third await statements in the SeedAsync no extra threads will be blocked, since already after the first await we are not blocking any useful threads and we already allocated an extra thread for the first await. So, by using the second and the third await statements we are allocating the extra two threads.
Am I missing something here? Since abp.io seems to be a big project I suspect that the examples would not be unreasonable and hence there is must be a reason to the use of the three await statements instead of the one.
Why do we need more than one await statement in a C# method?
You need as much await in your code, you want to (a)wait for the execution of the called async method completes. When you call an asynchronous method, it will (at some point!) return a task (incomplete or completed one), what is technically a promise from that method that at some point it will completes its job.
For example _bookRepository.InsertAsync(...) promise that it will insert the item into the repository and it will notify you via the returned Task when it is happened. It is now up to you, the caller, whether you want to wait for it using await, or you do not care if and when this job finished (fire and forget) so you do not use await, and continue to execute the rest of the caller code.
So it is totally valid to remove the await keywords almost everywhere but there is a very high chance it will alter the program flow and could lead to side effects (explanation in the next section).
In case we will remove the second and the third await statements in the SeedAsync no extra threads will be blocked, since already after the first await we are not blocking any useful threads and we already allocated an extra thread for the first await. So, by using the second and the third await statements we are allocating the extra two threads.
There are several misunderstanding here:
Calling an async method does not make the code asynchronous. The called code will run synchronously up until the called method, or any child method calls returns a Task, what is not already completed. Awaiting on completed task makes the call and the continuation completely synchronous!
Following up on the previous point, async method calls does not create or allocate thread per se. It is up to the called code to "side-load" its job one way or another.
Using await keyword will "put" the remaining code after the keyword into a continuation what will run asynchronously, but it say nothing about threads or necessarily creates one! It has to be imagined that the continuation is put into a queue and will be executed at some point by a thread.
there is must be a reason to the use of the three await statements instead of the one.
Without any further investigation into the project you quoted, most probably _bookRepository.InsertAsync(...) methods are not "parallel safe", otherwise await Task.WhenAll(insert1, insert2) format could have been used. Also not using await for the insertions potentially lead to side effect, for example multi threading like race conditions (read state before write has been finished).
EDIT:
You can find lots of useful reading material on learn.microsoft.com, such this: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model
I suggest to read them multiple times and make test apps because the topic is more complex than it looks like and filled with small details easy to misinterpret.
await will wait until the operation is not executed. So you has 2 async operations, that's why you need to use await.
One await for each async operation (method).
So, you have 3 async methods. You can call it without await, but it will crash. When you call it without await, it will start to execute in another thread, and thread where SeedAsync is executing, will not wait until InsertAsync is executed. It will start second InsertAsync at the same time
So, in your case, you can insert values without await. It will work. But in common case it's better to use await. Because often order of operations is important. await is allow to control the order of operations
Sometimes you need to run some tasks and then wait for all. Then you can use Task.WhenAll(t1,t2,t3)
If you remove the last two await, your code will become like this:
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() == 0)
{
_bookRepository.InsertAsync(new Book("Title1"));
_bookRepository.InsertAsync(new Book("Title2"));
}
}
Immediately you'll get two warnings:
Warning CS4014
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
The message of the warning is descriptive enough. Without these two await:
The two InsertAsync tasks will run concurrently. This could cause state corruption in case the InsertAsync manipulates shared state without synchronization.
The callers of the SeedAsync method will receive a Task that will signal its completion before it is actually completed.
Any unhandled exceptions that may occur during the execution of the two InsertAsync tasks will remain unobserved.
Yes you do.
When you await, your method is going into suspend mode. So during your first await, this block of code will stop running and will resume when it is appropriate (after the async code has been executed).
Microsoft docs on await
The await operator suspends evaluation of the enclosing async method
until the asynchronous operation represented by its operand completes.
When the asynchronous operation completes, the await operator returns
the result of the operation, if any.
If you remove the rest of the awaits, the code will not stop at their execution. It will just stop at the first one and fire and forget the other two. You should also have compiler warnings if you remove.
Check the explanation here. Without the await, the tasks will be run synchronously. What does that mean. Well long story short, when you wait the thread is open to doing other stuff. When you don't the thread can't be used for other actions outside the scope of the currently running code.
It's a fire and forget situation.
You can find an excellent read here on IO Threads to understand exactly how asynchronous programming works in C#
It is necessary to use await operator in all awaitable methods to gain advantage of asynchronous code.
In addition, await does not create new thread.
If you delete await operators, then your code will be still asynchronous, but these methods will be called sequentially, but you will not know when these methods will be finished to execute.
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
}
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
}
);
If _bookRepository.InsertAsync has the code that looks like this:
public async Task Add<T>(string url)
{
using (var context = new BloggingContext())
{
var blog = new Blog { Url = url };
context.Blogs.Add(blog);
await context.SaveChangesAsync();
}
}
then the above code will be executed on the current thread, but current thread will not be blocked until insertion in database is completed. So, e.g. user interface in desktop applications will not be frozen.
If your implementation of InsertAsync looks like this:
public async Task Add<T>(string url)
{
using (var context = new BloggingContext())
{
var blog = new Blog { Url = url };
await Task.Run( () =>
{
context.Blogs.Add(blog);
});
}
}
Then your code can be executed on other threads. CLR decides what thread will be used to perform Task.
As Microsoft docs says:
An example of synchronous code:
static void Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Egg eggs = FryEggs(2);
Console.WriteLine("eggs are ready");
Bacon bacon = FryBacon(3);
Console.WriteLine("bacon is ready");
Toast toast = ToastBread(2);
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
The computer will block on each statement until the work is complete
before moving on to the next statement. That creates an unsatisfying
breakfast. The later tasks wouldn't be started until the earlier tasks
had completed. It would take much longer to create the breakfast, and
some items would have gotten cold before being served.
If you want the computer to execute the above instructions
asynchronously, you must write asynchronous code.
Don't block, await instead
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Egg eggs = await FryEggs(2);
Console.WriteLine("eggs are ready");
Bacon bacon = await FryBacon(3);
Console.WriteLine("bacon is ready");
Toast toast = await ToastBread(2);
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
This code doesn't block while the eggs or the bacon are cooking. This
code won't start any other tasks though. You'd still put the toast in
the toaster and stare at it until it pops. But at least, you'd respond
to anyone that wanted your attention. In a restaurant where multiple
orders are placed, the cook could start another breakfast while the
first is cooking.
UPDATE:
When await operator is deleted, then we see the following warning in Visual Studio:
This async method lacks 'await' operators and will run synchronously.
Consider using the 'await' operator to await non-blocking API calls,
or 'await Task.Run(...)' to do CPU-bound work on a background thread.
You can try on your own:
static async void BarAsync()
{
Console.WriteLine("The start of FooMethod"); // synchronous
await Task.Run(() => FooMethod()); // asynchronous
Console.WriteLine("The end of FooMethod");
}
static void FooMethod()
{
Thread.Sleep(8000);
Console.WriteLine("Hello World");
}
I'm assuming that the other two functions that you are using are all asynchronous
the "InsertAsync" and "GetCountAsync"
Remember, when calling asynchronous functions you always have to await them. It means you have to wait for that function before next step can proceed running. Read more about async and await.
It is not required to have multiple await in a asynchronous function, but it is required to await a asynchronous function that you are calling inside of your function.
The awaits only allow you to wait for the operation/Task to be completed.
So answering your question, you don't need more than one. You can save all Tasks into variables ( var t1 = ...GetCountAsync()....var t2=..InsertAsync..) and then call await Task.WhenAll(t1,t2,t3) to wait for all tasks to be completed.
But it depends on the structure + behavior you want to give to your code. Maybe you need data to be persisted first and then use it on the next statement to call another method to pass it as a parameter, then you need to await for it first.
Hope this helps

What's the difference between starting and awaiting a Task?

What's the difference between starting and awaiting? Code below taken from Stephen Cleary's blog (including comments)
public async Task DoOperationsConcurrentlyAsync()
{
Task[] tasks = new Task[3];
tasks[0] = DoOperation0Async();
tasks[1] = DoOperation1Async();
tasks[2] = DoOperation2Async();
// At this point, all three tasks are running at the same time.
// Now, we await them all.
await Task.WhenAll(tasks);
}
I thought that the tasks begin running when you await them ... but the comments in the code seem to imply otherwise.
Also, how can the tasks be running after I just attributed them to an array of type Task. Isn't that just an attribution, by nature not involving action?
A Task returns "hot" (i.e. already started). await asynchronously waits for the Task to complete.
In your example, where you actually do the await will affect whether the tasks are ran one after the other, or all of them at the same time:
await DoOperation0Async(); // start DoOperation0Async, wait for completion, then move on
await DoOperation1Async(); // start DoOperation1Async, wait for completion, then move on
await DoOperation2Async(); // start DoOperation2Async, wait for completion, then move on
As opposed to:
tasks[0] = DoOperation0Async(); // start DoOperation0Async, move on without waiting for completion
tasks[1] = DoOperation1Async(); // start DoOperation1Async, move on without waiting for completion
tasks[2] = DoOperation2Async(); // start DoOperation2Async, move on without waiting for completion
await Task.WhenAll(tasks); // wait for all of them to complete
Update
"doesn't await make an async operation... behave like sync, in this example (and not only)? Because we can't (!) run anything else in parallel with DoOperation0Async() in the first case. By comparison, in the 2nd case DoOperation0Async() and DoOperation1Async() run in parallel (e.g. concurrency,the main benefits of async?)"
This is a big subject and a question worth being asked as it's own thread on SO as it deviates from the original question of the difference between starting and awaiting tasks - therefore I'll keep this answer short, while referring you to other answers where appropriate.
No, awaiting an async operation does not make it behave like sync; what these keywords do is enabling developers to write asynchronous code that resembles a synchronous workflow (see this answer by Eric Lippert for more).
Calling await DoOperation0Async() will not block the thread executing this code flow, whereas a synchronous version of DoOperation0 (or something like DoOperation0Async.Result) will block the thread until the operation is complete.
Think about this in a web context. Let's say a request arrives in a server application. As part of producing a response to that request, you need to do a long-running operation (e.g. query an external API to get some value needed to produce your response). If the execution of this long-running operation was synchronous, the thread executing your request would block as it would have to wait for the long-running operation to complete. On the other hand, if the execution of this long-running operation was asynchronous, the request thread could be freed up so it could do other things (like service other requests) while the long-running operation was still running. Then, when the long-running operation would eventually complete, the request thread (or possibly another thread from the thread pool) could pick up from where it left off (as the long-running operation would be complete and it's result would now be available) and do whatever work was left to produce the response.
The server application example also addresses the second part of your question about the main benefits of async - async/await is all about freeing up threads.
Isn't that just an attribution, by nature not involving action?
By calling the async method you execute the code within. Usually down the chain one method will create a Task and return it either by using return or by awaiting.
Starting a Task
You can start a Task by using Task.Run(...). This schedules some work on the Task Thread Pool.
Awaiting a Task
To get a Task you usually call some (async) Method that returns a Task. An async method behaves like a regular method until you await (or use Task.Run() ). Note that if you await down a chain of methods and the "final" method only does a Thread.Sleep() or synchronous operation - then you will block the initial calling thread, because no method ever used the Task's Thread Pool.
You can do some actual asynchronous operation in many ways:
using Task.Run
using Task.Delay
using Task.Yield
call a library that offers asynchronous operations
These are the ones that come to my mind, there are probably more.
By example
Let's assume that Thread ID 1 is the main thread where you are calling MethodA() from. Thread IDs 5 and up are Threads to run Tasks on (System.Threading.Tasks provides a default Scheduler for that).
public async Task MethodA()
{
// Thread ID 1, 0s passed total
var a = MethodB(); // takes 1s
// Thread ID 1, 1s passed total
await Task.WhenAll(a); // takes 2s
// Thread ID 5, 3s passed total
// When the method returns, the SynchronizationContext
// can change the Thread - see below
}
public async Task MethodB()
{
// Thread ID 1, 0s passed total
Thread.Sleep(1000); // simulate blocking operation for 1s
// Thread ID 1, 1s passed total
// the await makes MethodB return a Task to MethodA
// this task is run on the Task ThreadPool
await Task.Delay(2000); // simulate async call for 2s
// Thread ID 2 (Task's pool Thread), 3s passed total
}
We can see that MethodA was blocked on the MethodB until we hit an await statement.
Await, SynchronizationContext, and Console Apps
You should be aware of one feature of Tasks. They make sure to invoke back to a SynchronizationContext if one is present (basically non-console apps). You can easily run into a deadlock when using .Result or .Wait() on a Task if the called code does not take measures. See https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
async/await as syntactic sugar
await basically just schedules following code to run after the call was completed. Let me illustrate the idea of what is happening behind the scenes.
This is the untransformed code using async/await. The Something method is awaited, so all following code (Bye) will be run after Something completed.
public async Task SomethingAsync()
{
Hello();
await Something();
Bye();
}
To explain this I add a utility class Worker that simply takes some action to run and then notify when done.
public class Worker
{
private Action _action;
public event DoneHandler Done;
// skipping defining DoneHandler delegate
// store the action
public Worker(Action action) => _action = action;
public void Run()
{
// execute the action
_action();
// notify so that following code is run
Done?.Invoke();
}
}
Now our transformed code, not using async/await
public Task SomethingAsync()
{
Hello(); // this remains untouched
// create the worker to run the "awaited" method
var worker = new Worker(() => Something());
// register the rest of our method
worker.Done += () => Bye();
// execute it
worker.Run();
// I left out the part where we return something
// or run the action on a threadpool to keep it simple
}
Here's the short answer:
To answer this you just need to understand what the async / await keywords do.
We know a single thread can only do one thing at a time and we also know that a single thread bounces all over the application to various method calls and events, ETC. This means that where the thread needs to go next is most likely scheduled or queued up somewhere behind the scenes (it is but I won't explain that part here.) When a thread calls a method, that method is ran to completion before any other methods can be ran which is why long running methods are preferred to be dispatched to other threads to prevent the application from freezing. In order to break a single method up into separate queues we need to do some fancy programming OR you can put the async signature on the method. This tells the compiler that at some point the method can be broken up into other methods and placed in a queue to be ran later.
If that makes sense then you're already figuring out what await does... await tells the compiler that this is where the method is going to be broken up and scheduled to run later. This is why you can use the async keyword without the await keyword; although the compiler knows this and warns you. await does all this for you by use of a Task.
How does await use a Task tell the compiler to schedule the rest of the method? When you call await Task the compilers calls the Task.GetAwaiter() method on that Task for you. GetAwaiter() return a TaskAwaiter. The TaskAwaiter implements two interfaces ICriticalNotifyCompletion, INotifyCompletion. Each has one method, UnsafeOnCompleted(Action continuation) and OnCompleted(Action continuation). The compiler then wraps the rest of the method (after the await keyword) and puts it in an Action and then it calls the OnCompleted and UnsafeOnCompleted methods and passes that Action in as a parameter. Now when the Task is complete, if successful it calls OnCompleted and if not it calls UnsafeOnCompleted and it calls those on the same thread context used to start the Task. It uses the ThreadContext to dispatch the thread to the original thread.
Now you can understand that neither async or await execute any Tasks. They simply tell the compiler to use some prewritten code to schedule all of it for you. In fact; you can await a Task that's not running and it will await until the Task is executed and completed or until the application ends.
Knowing this; lets get hacky and understand it deeper by doing what async await does manually.
Using async await
using System;
using System.Threading.Tasks;
namespace Question_Answer_Console_App
{
class Program
{
static void Main(string[] args)
{
Test();
Console.ReadKey();
}
public static async void Test()
{
Console.WriteLine($"Before Task");
await DoWorkAsync();
Console.WriteLine($"After Task");
}
static public Task DoWorkAsync()
{
return Task.Run(() =>
{
Console.WriteLine($"{nameof(DoWorkAsync)} starting...");
Task.Delay(1000).Wait();
Console.WriteLine($"{nameof(DoWorkAsync)} ending...");
});
}
}
}
//OUTPUT
//Before Task
//DoWorkAsync starting...
//DoWorkAsync ending...
//After Task
Doing what the compiler does manually (sort of)
Note: Although this code works it is meant to help you understand async await from a top down point of view. It DOES NOT encompass or execute the same way the compiler does verbatim.
using System;
using System.Threading.Tasks;
namespace Question_Answer_Console_App
{
class Program
{
static void Main(string[] args)
{
Test();
Console.ReadKey();
}
public static void Test()
{
Console.WriteLine($"Before Task");
var task = DoWorkAsync();
var taskAwaiter = task.GetAwaiter();
taskAwaiter.OnCompleted(() => Console.WriteLine($"After Task"));
}
static public Task DoWorkAsync()
{
return Task.Run(() =>
{
Console.WriteLine($"{nameof(DoWorkAsync)} starting...");
Task.Delay(1000).Wait();
Console.WriteLine($"{nameof(DoWorkAsync)} ending...");
});
}
}
}
//OUTPUT
//Before Task
//DoWorkAsync starting...
//DoWorkAsync ending...
//After Task
LESSON SUMMARY:
Note that the method in my example DoWorkAsync() is just a function that returns a Task. In my example the Task is running because in the method I use return Task.Run(() =>…. Using the keyword await does not change that logic. It's exactly the same; await only does what I mentioned above.
If you have any questions just ask and I'll be happy to answer them.
With starting you start a task. That means it might be picked up for execution by whatever Multitasaking system is in place.
With waiting, you wait for one task to actually finish before you continue.
There is no such thing as a Fire and Forget Thread. You always need to come back, to react to exceptions or do somethings with the result of the asynchronous operation (Database Query or WebQuery result, FileSystem operation finished, Dokument send to the nearest printer pool).
You can start and have as many task running in paralell as you want. But sooner or later you will require the results before you can go on.

How is async with await different from a synchronous call?

I was reading about asynchronous function calls on Asynchronous Programming with Async and Await.
At the first example, they do this, which I get:
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
string urlContents = await getStringTask;
But then they explain that if there's not any work to be done in the mean time, you can just do it like this:
string urlContents = await client.GetStringAsync();
From what I understand, the await keyword will suspend the code flow until the function returns. So how is this different from the synchronous call below?
string urlContents = client.GetString();
Calling await client.GetStringAsync() yields the execution to the calling method, which means it won't wait for the method to finish executing, and thus won't block the thread. Once it's done executing in the background, the method will continue from where it stopped.
If you just call client.GetString(), the thread's execution won't continue until this method finished executing, which will block the thread and may cause the UI to become unresponsive.
Example:
public void MainFunc()
{
InnerFunc();
Console.WriteLine("InnerFunc finished");
}
public async Task InnerFunc()
{
// This causes InnerFunc to return execution to MainFunc,
// which will display "InnerFunc finished" immediately.
string urlContents = await client.GetStringAsync();
// Do stuff with urlContents
}
public void InnerFunc()
{
// "InnerFunc finished" will only be displayed when InnerFunc returns,
// which may take a while since GetString is a costly call.
string urlContents = client.GetString();
// Do stuff with urlContents
}
From what I understand, the await keyword will suspend the code flow until the function returns
Well, Yes and No.
Yes, because code flow does stop in a sense.
No, because the thread executing this code flow does not block. (The synchronous call client.GetString() will block the thread).
In fact, it will return to its calling method. To understand what it means by return to its calling method, you can read about another C# compiler magic - the yield return statement.
Iterator blocks with yield return will break the method into a state machine - where code after the yield return statement will execute only after MoveNext() is called on the enumerator. (See this and this).
Now, async/await mechanism is also based on similar state machine (however, its much more complicated than the yield return state machine).
To simplify matters, lets consider a simple async method:
public async Task MyMethodAsync()
{
// code block 1 - code before await
// await stateement
var r = await SomeAwaitableMethodAsync();
// code block 2 - code after await
}
When you mark a method with async identifier you tell the compiler to break the method into a state machine and that you are going to await inside this method.
Lets say code is running on a thread Thread1 and your code calls this MyMethodAsync(). Then code block 1 will synchronously run on the same thread.
SomeAwaitableMethodAsync() will also be called synchronously - but lets say that method starts a new asynchronous operation and returns a Task.
This is when await comes into picture. It will return the code flow back to its caller and the thread Thread1 is free to run callers code. What happens then in calling method depends on whether calling method awaits on MyMethodAsync() or does something else - but important thing is Thread1 is not blocked.
Now rest of await's magic - When the Task returned by SomeAwaitableMethodAsync() eventually completes, the code block 2 is Scheduled to run.
async/await is built on the Task parallel library - so, this Scheduling is done over TPL.
Now the thing is that this code block 2 may not be scheduled over the same thread Thread1 unless it had an active SynchronizationContext with thread affinity (like WPF/WinForms UI thread). await is SynchronizationContext aware, so, code block 2 is scheduled over same SynchronizationContext, if any, when the MyMethodAsync() was called. If there was no active SynchronizationContext, then with all possibility, code block 2 will run over some different thread.
Lastly, I will say that since async/await is based on state machine created by compiler, like yield return, it shares some of the shortcomings - for example, you cannot await inside a finally block.
I hope this clears your doubts.

Categories

Resources