How to execute multiple async Tasks concurrently - c#

I'm confused by this code.
According to the documentation that includes this code,
it invokes each task which allows them to execute
concurrently, and then awaits for them both to be complete.
I don't understand how the order of execution works. By declaring the task var firstTask = DoSomethingAsync(); does this cause the task to begin executing? I thought it is synchronous without an await expression.
public async Task RunConcurrentTasks()
{
var firstTask = DoSomethingAsync();
var secondTask = DoSomethingElseAsync();
await firstTask;
await secondTask;
}
Also, does this code achieve the same result?
public async Task RunConcurrentTasks()
{
var firstTask = DoSomethingAsync();
var secondTask = DoSomethingElseAsync();
await Task.WhenAll(firstTask, secondTask);
}

var firstTask = DoSomethingAsync(); does this cause the task to begin executing?
Yes. It had better! DoSomethingAsync promises in its name to do something asynchronously, so if it does not hand you back an executing task, then its name is a lie.
How then does execution advance to the next line to invoke the second task?
I don't understand the question.
I thought it is synchronous without an await expression.
Your belief is false. "Await" does not mean "make this asynchronous". Await neither creates nor destroys asynchrony; it merely manages existing asynchrony. DoSomethingAsync is already asynchronous independent of any await. Again, it says that it does something asynchronously, so that had better be what it does!
await in your example is just return. It means "return to my caller, and start running this method again at some time in the future after the awaited task is finished." It's an asynchronous wait.
Think of await as being like yield return. When you're in an iterator block and you encounter a yield return, the method returns, and then it picks up where it left off at some point in the future. await is basically the same; just the mechanisms are slightly different.
Another way to think of it is that await marks a point where the current method cannot continue to run until the awaited task is completed. Since the method cannot continue to run, it returns.
Again, await does not do anything to a call on its right hand side. It's just an operator, and it obeys all the normal rules of operators:
await Foo();
and
var blah = Foo();
await blah;
are the same thing, just like
var x = Foo() + Bar();
and
var f = Foo();
var b = Bar();
var x = f + b;
are the same thing. Await does not do something magical to its operands any more than + does something magical to its operands. It's just a special kind of return.
Also, does this code do the same thing?
Please don't ask two questions in one question. Post two questions if you have two questions.
The question is unclear. The semantics of both workflows are that the task returned by RunConcurrentTasks is signaled as being completed when both its child tasks are complete, if that's what you're asking.

By declaring the task var firstTask = DoSomethingAsync(); does this cause the task to begin executing?
Yes.
How then does execution advance to the next line to invoke the second task?
It will run DoSomethingAsync until it hits its first await that yields. At that point, DoSomethingAsync returns a task that has not yet completed, and the caller RunConcurrentTasks continues to the next line.
I thought it is synchronous without an await expression.
No, it's still asynchronous. await doesn't run anything. await will cause the consuming code to asynchronously wait ("await") for the task to complete.
You may find my async intro helpful.

Task Documentation
The Task class represents a single operation that does not return a value and that usually executes asynchronously. Task objects are one of the central components of the task-based asynchronous pattern first introduced in the .NET Framework 4. Because the work performed by a Task object typically executes asynchronously on a thread pool thread rather than synchronously on the main application thread...
Here is your code annotated, hopefully this makes more sense.
public async Task RunConcurrentTasks()
{
// starts DoSomethingAsync and returns a Task which contains the information about the running state of that operation
// the thread that called this gets that Task as a result immediatly and can continue on to the next statement while this is running
var firstTask = DoSomethingAsync();
// starts DoSomethingElseAsync and returns a Task which contains the information about the running state of that operation
// the thread that called this gets that Task as a result immediatly and can continue on to the next statement while this is running
var secondTask = DoSomethingElseAsync();
// suspend the execution of the method until the awaited task completes
// only then do we proceed to the next line (assuming no exception was thrown)
await firstTask;
// suspend the execution of the method until the awaited task completes
// only then do we proceed to the next line (assuming no exception was thrown)
await secondTask;
}
public async Task RunConcurrentTasks()
{
// starts DoSomethingAsync and returns a Task which contains the information about the running state of that operation
// the thread that called this gets that Task as a result immediatly and can continue on to the next statement while this is running
var firstTask = DoSomethingAsync();
// starts DoSomethingElseAsync and returns a Task which contains the information about the running state of that operation
// the thread that called this gets that Task as a result immediatly and can continue on to the next statement while this is running
var secondTask = DoSomethingElseAsync();
// suspend the execution of the method until the awaited DoSomethingAsync AND DoSomethingElseAsync have completed
// only then do we proceed to the next line (assuming no Exceptions was thrown)
await Task.WhenAll(firstTask, secondTask);
}
From comments
How would I do it synchronously, if I wanted to?
Await documentation
The await operator is applied to a task in an asynchronous method to suspend the execution of the method until the awaited task completes
Use await immediately after each call, this ensures that execution does not proceed to the next line until a result (or Exception) is returned from the execution of the Task being awaited. It does not actually make it synchronous in the technical sense of the word. So take your 1st example and reorder the lines so that you call await after starting the first operation and before you start your 2nd operation.
var firstTask = DoSomethingAsync();
await firstTask;
var secondTask = DoSomethingElseAsync();
await secondTask;
which can also be rewritten as
await DoSomethingAsync();
await DoSomethingElseAsync();

upon invoking the async method, with the parenthesys (), such as DoSomethingAsync(); will begin the execution of the async functionality. It will return a Task immediately, which contains information about the async functionality being executed, and lets the system know when it is finished as well.
To run them in order, just await each one individually instead of awaiting the WhenAll call. As such:
public async Task RunConcurrentTasks()
{
await DoSomethingAsync();
await DoSomethingElseAsync();
}
now DoSomethingElseAsync will wait on the call to DoSomethingAsync before executing.

Related

C# Will code execution continue after await?

After a lot of readings about await/async, I still have some misunderstanding about the subject.
Please provide a short answer (yes/no) and a long answer to my questions, so I can have better understanding.
Let's say we have the following method:
public async Task UnresultTaskMethod()
{
await AsyncMethod1();
await AsyncMethod2();
}
Questions:
When the execution of the code will reach AsyncMethod1, will it automatically move to call AsyncMethod2 while AsyncMethod1() is getting executed?
When the execution of the code will reach AsyncMethod2, will the execution of the method ends, and the execution will return to the caller, before the execution of AsyncMethod2 completely ends?
Is there such a cases where the execution of the method will end before the calls to async method fully completes (for example the method was void instead of task)?
When I will need to put Task.WaitAll(tasks); Task.WhenAll(tasks); at the end so I can make sure the execution will not continue before all the tasks have ended, this is the main factor of confusion for me, why to wait for a task in this way if you can wait for them by keyword await?
I was thinking when the execution will hit async method with await, it will put it on processing schedule, and continue to execute the code inside the function, I'm feeling that I misunderstood.
await roughly means 'wait until completed'
No
No
If you don't await, it might be the case, for example
public async Task UnresultTaskMethod()
{
Task.Delay(2000);
// We haven't awaited, so we're here right away
await AsyncMethod2();
}
Task.WaitAll and Task.WhenAll don't make sense when you await individual tasks right away. However, you can do this:
public async Task UnresultTaskMethod()
{
var task1 = AsyncMethod1();
var task2 = AsyncMethod2();
// The tasks are now doing their job
await Task.WhenAll(task1, task2);
// Here you are sure both task1 and task2 are completed
}
With Task.WaitAll it would be like this:
public async Task UnresultTaskMethod()
{
var task1 = AsyncMethod1();
var task2 = AsyncMethod2();
// This is not awaitable, you're blocking the current thread
Task.WaitAll(task1, task2);
// Here you are sure both task1 and task2 are completed
}
In this case, you don't need async Task, because you are not awaiting, i.e. it is effectively a void method.
Hopefully now it is a bit clearer

Does calling an async method immediately running its commands

I got this code:
static async Task AsynchronousProcessing()
{
Task<string> t1 = GetInfoAsync("Task 1", 3);
Task<string> t2 = GetInfoAsync("Task 2", 5);
string[] results = await Task.WhenAll(t1, t2);
foreach (string result in results)
{
WriteLine(result);
}
}
static async Task<string> GetInfoAsync(string name, int seconds)
{
await Task.Delay(TimeSpan.FromSeconds(seconds));
return "hi";
}
When reaching the line Task<string> t1 = GetInfoAsync("Task 1", 3);, will it already start asynchronously with the await Task.Delay(TimeSpan.FromSeconds(seconds)); and move on to the rest of the method, or will it return immediatly a task without actually starting it?
And if it won't start when calling the method, will the tasks start only when calling the Task.WhenAll(t1,t2)? Since if it doesn't start right away, I would expect to see GetInfoAsync doing something like return new Task<string>(() => ...);
I recommend reading my async intro.
In summary, all async methods begin executing synchronously. So when AsynchronousProcessing calls GetInfoAsync, then GetInfoAsync begins running, synchronously, just like any other method. An async method can become asynchronous when there is an await. In this case, GetInfoAsync calls Task.Delay (again, synchronously, just like a regular method call), and then passes its Task to await. At this point, await will examine the task; if it is already complete, then it will continue running synchronously; otherwise, it will act asynchronously and return an incomplete task from GetInfoAsync.
The tasks returned from async methods are in progress. I don't usually use the term "running", because they are not actually running code on a CPU and because the task status is not actually Running. This "asynchronous in-progress" state is unfortunately named WaitingForActivation (even though it's not waiting for anything).
Since if it doesn't start right away, I would expect to see GetInfoAsync doing something like return new Task(() => ...);
The async keyword handles the creation of the task. As noted above, the tasks are "hot" or "in progress". This is not the same as "running" or "started", both of which imply tasks that run CPU code. There are two types of tasks: what I call Delegate Tasks and Promise Tasks. The ones returned from async methods are Promise Tasks.
If there aren't any new threads with await, then how will they run in parallel?
They run concurrently (not in parallel, since there is no additional thread blocked on the delay).
However, when the await Task.Delay(..) completes, then there could possibly be another thread used. The await will resume on its captured context and execute the return "hi"; in that context. If the captured context is a thread pool context, then that one line of code will be executed on a thread pool thread. The async state machine translates the return into code that completes the task that was previously returned from that async method.
GetInfoAsync is called and runs synchronously just as usual until it hits the await Task.Delay(TimeSpan.FromSeconds(seconds)) line. It then returns an uncompleted task back to the calling AsynchronousProcessing() method.
So, yes, the task is indeed being started when you call the method, i.e. you don't have to await GetInfoAsync for the Task.Delay method to get called.

When does await examine the awaitable?

Let me quote the code snippet and its relevant explanation from Async and Await article written by Stephen Cleary as follows.
public async Task DoSomethingAsync()
{
// In the Real World, we would actually do something...
// For this example, we're just going to (asynchronously) wait 100ms.
await Task.Delay(100);
}
The beginning of an async method is executed just like any other method. That is, it runs synchronously until it hits an “await” (or throws an exception).
The “await” keyword is where things can get asynchronous. Await is like a unary operator: it takes a single argument, an awaitable (an “awaitable” is an asynchronous operation). Await examines that awaitable to see if it has already completed; if the awaitable has already completed, then the method just continues running (synchronously, just like a regular method).
If “await” sees that the awaitable has not completed, then it acts asynchronously. It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method.
To accommodate more general case, let me define the code snippet as follows.
public async Task DoSomethingAsync()
{
AAA();
await BBBAsync;
CCC();
}
where BBBAsync is the only "awaitable" operation or method.
For the sake of simplicity, let's assume we execute DoSomethingAsync from within the main app thread. So in my understanding,
AAA() will always be executed synchronously.
BBBAsync() will always be executed asynchronously no matter how DoSomethingAsync() is invoked.
CCC() may be executed either asynchronously or synchronously depending on the result of the await operator determines.
It seems to me, the await operator always find the awaitable has not been completed yet because await always comes before BBBAsync() starts.
Question
When does await examine the awaitable?
It sounds like you think async makes method asynchronous which is not the case. Async only changes how code for the method is generated, but by itself it does not add any asynchronous steps nor run the method on another thread. See many questions like What is the difference between asynchronous programming and multithreading?.
await examines awaitable at the same time irrespective whether it completed yet or not - method like BBBAsync() will eventually return Task that may be either completed or not, but method will return.
Here is an example of task completing synchronously:
async Task BBBAsync() // ignore CS1998 as we don't want `await` inside
{
return;
}
// alternatively the same behavior just returning completed Task
// with FromResult or Task.CompletedTask for 4.6 and later
Task BB2Async()
{
return Task.FromResult(0);
}
You can await such task just fine, but this BBBAsync will complete synchronously and await will pick synchronous branch to continue.
More realistic sample would be returning recently cached value synchronously. This code will generate full state machine for asynchronous execution, but every other call will return synchronously thus making corresponding await to continue synchronously too.
async Task<int> GetValueAsync()
{
if (canReturnCached)
{
canReturnCached = false;
return cachedValue;
}
cachedValue = await SlowRemoteCallForValueAsync();
canReturnCached = true;
return cachedValue;
}

multiple awaits vs Task.WaitAll - equivalent?

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.

How Async and Await works

I am trying to understand how Async and Await works. How control travel in the program. Here is the code which I was trying to understand.
public async Task MyMethod()
{
Task<int> longRunningTask = LongRunningOperation();
//indeed you can do independent to the int result work here
MySynchronousMethod();
//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}
public async Task<int> LongRunningOperation() // assume we return an int from this long running operation
{
await Task.Delay(5000); //5 seconds delay
return 1;
}
private void Button_Click_3(object sender, RoutedEventArgs e)
{
MyMethod();
}
When button click occur then MyMethod() will be called and from the MyMethod LongRunningOperation() will be called and it take 5 sec to complete. so my question is
What is the meaning of this line
Task longRunningTask = LongRunningOperation();
what this does int result = await longRunningTask;
The above line could be committed and one line we can construct like
Task<int> longRunningTask = await LongRunningOperation();
or
int result = await longRunningTask;
Please can someone explain to me the above code which is not clear to me.
1) if the longRunningOperation hasn't finished and is still running, MyMethod() will return to its calling method, thus the main thread doesn't get blocked. When the longRunningOperation is done then a thread from the ThreadPool (can be any thread) will return to MyMethod() at its previous state and continue execution (in this case printing the result to the console).
A second case would be that the longRunningOperation has already finished its execution and the result is available. When reaching the await longRunningOperation the compiler knows that it has the result and will keep on executing code on the very same thread. (in this case printing result to console).
point 1 is not at all clear to me like the statement "if the longRunningOperation hasn't finished and is still running, MyMethod() will return to its calling method"
if possible explain the point one in more detail. thanks
I've been taught about it in the following fashion, I found it to be quite a clear and concise explanation:
//this is pseudocode
async Method()
{
code;
code;
await something;
moreCode;
}
When Method is invoked, it executes its contents (code; lines) up to await something;. At that point, something; is fired and the method ends like a return; was there.
something; does what it needs to and then returns.
When something; returns, execution gets back to Method and proceeds from the await onward, executing moreCode;
In a even more schematic fashion, here's what happens:
Method is invoked
code; is executed
something; is executed, flow goes back to the point where Method was invoked
execution goes on with what comes after the Method invocation
when something; returns, flow returns inside Method
moreCode; is executed and Method itself ends (yes, there could be something else await-ing on it too, and so on and so forth)
I have an async intro on my blog that you may find helpful.
This code:
int result = await LongRunningOperation();
is essentially the same as this code:
Task<int> resultTask = LongRunningOperation();
int result = await resultTask;
So, yes, LongRunningOperation is invoked directly by that method.
When the await operator is passed an already-completed task, it will extract the result and continue executing the method (synchronously).
When the await operator is passed an incomplete task (e.g., the task returned by LongRunningOperation will not be complete), then by default await will capture the current context and return an incomplete task from the method.
Later, when the await task completes, the remainder of the method is scheduled to run in that context.
This "context" is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current. If you're running this in a Console app, then the context is usually the thread pool context, so the async method will resume executing on a thread pool thread. However, if you execute the same method on a UI thread, then the context is a UI context and the async method will resume executing on the UI thread.
Behind the scenes C# compiler actually converts your code into a state machine. It generates a lot more code so that behind the scenes every time a await task or async action is completed, it'll continue execution from where it left off. In terms of your question, every time the async action has finished, the async method will be called back on the calling thread when you originally started the call to the async method. Eg it'll execute your code on the thread that you started on. So the async action will be run on a Task thread, then the result will be returned back on the thread you method was originally called on and keep executing.
Await will get the value from the Task or async action and "unbox" it from the task when the execution is returned. In this case it will automatically put it into the int value, so no need to store the Task<int>.
Your code has the problem where it await's on the LongRunningTask() you'd most likely just want to return the long task method without the async, then have your MyMethod perform the await.
int value = await LongWaitingTask()
Async Await and the Generated StateMachine
It's a requirement of async methods that you return a Task or void.
It's possible to change it so when you return back from executing the async task it will execute the remaining code on the thread the async task was performed on using the Task.ConfigureAwait method.
It may be easier to think about it this way:
Whenever you have an await, the compiler splits your method into 2: one part before the await and another part after it.
The second part runs after the first one has finished successfully.
In your code, the first method will look like something roughly equivalent to this:
public async Task MyMethod()
{
Task<int> longRunningTask = LongRunningOperation();
MySynchronousMethod();
longRunningTask.ContinueWith(t => part2(t.Result));
}
void part2(int result)
{
Console.WriteLine(result);
}
Few important notes:
It's obviously much more complex than this since it should support
try-catch and others. Also, it doesn't really use the task
Task is not actually being used directly. It's using the task's GetAwaiter() method and its API, or any other class with this method or extension method.
If there are multiple awaits in a method it's being split multiple times.
If MyMethod is being split, how does someone who awaits MyMethod knows when all parts are done? When your async method returns a Task, the compiler generates a special Task which tracks everything with a state machine.

Categories

Resources