Does await keyword block execution of current method? [duplicate] - c#

Let's say I have two async methods
public async static Task RunAsync1()
{
await Task.Delay(2000);
await Task.Delay(2000);
}
and
public async static Task RunAsync2()
{
var t1 = Task.Delay(2000);
var t2 = Task.Delay(2000);
await t1;
await t2;
}
Then I use it like
public static void M()
{
RunAsync1().GetAwaiter().GetResult();
RunAsync2().GetAwaiter().GetResult();
}
In a result the RunAsync1 will run 4sec but RunAsync2 only 2sec
Can anybody explain why? Methods are almost the same. What is the difference?

In the second method 2 tasks are started at the same time. They will both finish in 2 seconds (as they are running in parallel). In the first method you first run one method (2 seconds), wait for it to complete, then start the second one (2 more seconds). The key point here is Task.Delay(..) starts right when you call it, not when you await it.
To clarify more, first method:
var t1 = Task.Delay(2000); // this task is running now
await t1; // returns 2 seconds later
var t2 = Task.Delay(2000); // this task is running now
await t2; // returns 2 more seconds later
Second method:
var t1 = Task.Delay(2000);
var t2 = Task.Delay(2000); // both are running now
await t1; // returns in about 2 seconds
await t2; // returns almost immediately, because t2 is already running for 2 seconds

Just examine your code:
public async static Task RunAsync1()
{
await Task.Delay(2000); // Start a delay task, and WAIT for it to finish
await Task.Delay(2000); // Start a delay task, and WAIT for it to finish
}
So the second await Task.Delay(2000); is called after the first call is finished (after 2 seconds).
While the second method,
public async static Task RunAsync2()
{
var t1 = Task.Delay(2000); // Start a task
var t2 = Task.Delay(2000); // Start a task
await t1; // Wait for task to finish
await t2; // Wait for task to finish
}
So tasks t1, and t2 run at the same time.
If you change it to
public async static Task RunAsync3()
{
var t1 = Task.Delay(2000); // Start a task
await t1; // Wait for task to finish
var t2 = Task.Delay(2000); // Start a task
await t2; // Wait for task to finish
}
you would get the same results as in RunAsync1.

In the first case you are saying
public async static Task RunAsync1()
{
var t1 = Task.Delay(2000);
await t1;
var t2 = await Task.Delay(2000);
await t2;
}
Which equates to
0:00 Create a callback in 2 seconds 0:00
0:00 Wait until the callback has returned 0:02
0:02 Create a callback in 2 seconds 0:02
0:02 Wait until the callback has returned 0:04
0:04 return;
The second case is
public async static Task RunAsync2()
{
var t1 = Task.Delay(2000);
var t2 = Task.Delay(2000);
await t1;
await t2;
}
0:00 Create callbacks in 2 seconds 0:00
0:00 Create callbacks in 2 seconds 0:00
0:00 Wait for first callback 0:02
0:02 Wait for the second callback 0:02
0:02 return
In other words, in the first one you are doing sequential asynchronous programming, and the second is parallel asynchronous programming.

Whenever you start a Task. It already started when you created it, not when you called await.
If you create a task and put it in a variable, it might already finish when you await that. This is what happen to your second case. await just ensures that it must finish before continuing.

Related

Await doesn't seem to execute sequentially

In C#,
static async Task<int> Task1()
{
await Task.Delay(1000); // delaying 1 sec
return 10;
}
static async Task<int> Task2()
{
await Task.Delay(2000); // delaying 2 sec
return 10;
}
static async Task<int> Task3()
{
await Task.Delay(3000); // delaying 3 sec
return 10;
}
when performing below operations it takes 6 sec
int a = await Task1();
int b = await Task2();
int c = await Task3();
return a+b+c;
but when performing below operations it takes only 3 sec
var a = Task1();
var b = Task2();
var c = Task3();
return await a + await b + await c;
results are getting as 30 from both the snippet but duration differs...
Can anyone please clarify it
reference - https://dotnetfiddle.net/I31eqK
When you do this:
int a = await Task1();
int b = await Task2();
int c = await Task3();
return a+b+c;
each task is awaited before the next one is started. That is, each task must have returned a result before the next task is awaited. Thus, the total time is the total time for all tasks.
But when you do this:
var a = Task1();
var b = Task2();
var c = Task3();
return await a + await b + await c;
the three tasks are started in parallel, and THEN you await their results. Thus in this case the total time is whatever the longest task takes.
The code below works synchronously. Meaning that you are queuing up the tasks in series (therefore executing them one-by-one) as the functions wait patiently for the ones above them to finish like gentlemen.
Figuratively: "waiting for one to finish their sentence before speaking".
int a = await Task1(); // wait for the above function/Task to complete THEN run this one ("wait for one to finish their sentence THEN talk")
int b = await Task2(); // wait for the above function/Task to complete THEN run this one ("wait for one to finish their sentence THEN talk")
int c = await Task3(); // wait for the above function/Task to complete THEN run this one ("wait for one to finish their sentence THEN talk")
return a+b+c; // return the values received
In the code below, Instead of waiting the function to finish and return one-by-one, you are instead ("asynchronously") waiting for all of the included variables to have a value (as you are not waiting for one function to finish before running the next one, but rather waiting for every necessary value to exist).
Figuratively: "Talking over each other, therefore ending the conversation earlier than waiting for one to finish their sentence".
var a = Task1(); // Run this function while other functions are running ("Talk while other people are talking")
var b = Task2(); // Run this function while other functions are running ("Talk while other people are talking")
var c = Task3(); // Run this function while other functions are running ("Talk while other people are talking")
return await a + await b + await c; // now wait for every variable to have their own value and THEN return
Examples
Here's an example to make this easy to understand:
int a = Task1(); // Asynchronous call ("Dont wait for someone to finish speaking")
int b = Task2(); // Asynchronous call ("Dont wait for someone to finish speaking")
int c = await Task3(); // Synchronous call ("wait for someone/people to finish talking)
The code above should take 5 seconds because:
Task1() pauses for 1000 milliseconds and runs at the same time as Task2() with its 2000 milliseconds (as Task1 and Task2 are not waiting for each other to finish), As Task1 Finishes in 1 second where as Task2 will finish in 2 Seconds, therefore taking it 2 seconds before Task3 is called
Task3(), with its 3000 millisecond pause, will wait patiently until all functions before it are done with their own thing. Because Task2 is finished, Task3 is then called, adding an additional 3 seconds to the 2 seconds Total.
Extra Note: Please run this script (not on dotnetfiddle.com as it doesn't show the live output but rather on your local machine): https://www.toptal.com/developers/hastebin/igapunubis.csharp
With
var a = Task1(); // Started here
var b = Task2(); // Started here
var c = Task3(); // Started here
var r = await a + await b + await c;
the tasks are started pretty much at the same time.
With:
int a = await Task1();
int b = await Task2();
int c = await Task3();
return a+b+c;
the 2nd task starts only when the first one finishes.
The following code would take ~6 seconds:
Func<Task<int>> a = async () => await Task1(); // Not started here
var b = async () => await Task2(); // Not started here
var c = async () => await Task3(); // Not started here
var r = await a() + await b() + await c();
Console.WriteLine(r);
When you do the following:
int a = await Task1();
int b = await Task2();
int c = await Task3();
You are awaiting the task to complete, in other words, once execution of Task1 starts, it will not execute Task2 untill Task1 completes. You are awaiting the Task to complete.
When you do:
var a = Task1();
var b = Task2();
var c = Task3();
You are not awaiting for each individual task to complete, instead, you are executing the Tasks, and the thread pool manages these tasks, and in some cases, can run them simultaniously, resulting in faster times. For example, if Task1, Task2, Task3 are run in parralel, best case scenario, is that it will take 3 seconds to complete.

It's possible to give priority to a certain Task when executing more than one?

I'm executing an async function (ExampleFunction) that blocks the thread for the following calls if the previous one hasn't finished.
I'm printing messages in the console to check the order, but the order is not the expected.
So, this is the code:
private async Task TestTest()
{
int idtmp = id;
var task1 = Test(idtmp);
id++;
int idtmp2 = id;
var task2 = Test(idtmp2);
id++;
await Task.WhenAll(task1, task2).ConfigureAwait(false);
Console.ReadLine();
}
private async Task Test(int id)
{
Console.WriteLine($"Before calling {id} {DateTime.Now.ToString("HH:mm:ss.ffff")}");
Task<int> task = ExampleFunction();
Console.WriteLine($"After calling {id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
await task.ConfigureAwait(false);
Console.WriteLine($"Function finished {id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
}
This is the output:
Before calling 1 17:23:42.5953
After calling 1 17:23:42.6356
Before calling 2 17:23:42.6371
After calling 2 17:24:07.0415
Function finished 1 17:24:07.0453
Function Finished 2 17:31.2036
The expected output should be:
Before calling 1
After calling 1
Before calling 2
Function finished 1
After calling 2
Function Finished 2
But it seems that when the first Task finishes, the second one continues until it hits the await and that's why it shows the After calling 2 message first.
Is there a way to specify that the Task1 has priority when the first call finishes?
EDIT:
The ExampleFunction has two behaviors in the server side:
The second call will block the thread if there is another call in progress
The second call won't block the thread, it will be queued in the background.
So, for the second behavior the output should be this:
Before calling 1
After calling 1
Before calling 2
After calling 2
Function finished 1
Function Finished 2
But that's the output that I'm receiving with the first behavior.
I need to check the correct behavior in each configuration.
That's why the current method doesn't work for me.
If I've understood you correctly, the key thing here is that you want to start the second task at the same time as the first to let service calls begin, but you can't finish the second task until you have the results of the first.
If so, your second task needs to look like this:
public async Task Task2(int id, Task<Task1State> task1)
{
/* Start Service calls */
await Task.Delay(TimeSpan.FromSeconds(1.0));
var state = await task1;
/* Complete `Task2` with the `state` from `Task1` */
await Task.Delay(TimeSpan.FromSeconds(1.0));
}
The await Task.Delay calls are there to simulate work.
Now it's easy to harness up a test:
async Task Main()
{
var id = 0;
var task1_id = ++id;
var task2_id = ++id;
Console.WriteLine($"Before calling {task1_id} {DateTime.Now.ToString("HH:mm:ss.ffff")}");
var task1 = Task1(task1_id);
Console.WriteLine($"After calling {task1_id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
Console.WriteLine($"Before calling {task2_id} {DateTime.Now.ToString("HH:mm:ss.ffff")}");
var task2 = Task2(task2_id, task1);
Console.WriteLine($"After calling {task2_id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
await task2;
Console.WriteLine($"Function finished {task2_id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
}
public class Task1State { }
public async Task<Task1State> Task1(int id)
{
await Task.Delay(TimeSpan.FromSeconds(2.0));
Console.WriteLine($"Function finished {id} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
return new Task1State();
}
When I run that I get the following expected results:
Before calling 1 14:16:13.0724
After calling 1 14: 16:13.0736
Before calling 2 14:16:13.0736
After calling 2 14: 16:13.0743
Function finished 1 14: 16:15.0889
Function finished 2 14: 16:16.1039
The combined wait time is 4 seconds, but because two of those seconds happen in parallel then the total time is 3 seconds as seen in the output.
Async methods will run synchronously until they hit the first await of an incomplete task. If you think about how much of your code might run synchronously at each await it would be equivalent to;
int idtmp = id;
Console.WriteLine($"Before calling {idtmp} {DateTime.Now.ToString("HH:mm:ss.ffff")}");
var task1 = ExampleFunction();
Console.WriteLine($"After calling {idtmp} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
if (task1.IsCompleted){
Console.WriteLine($"Function finished {idtmp} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
}else{
// async magic
}
id++;
int idtmp2 = id;
Console.WriteLine($"Before calling {idtmp2} {DateTime.Now.ToString("HH:mm:ss.ffff")}");
var task2 = ExampleFunction();
Console.WriteLine($"After calling {idtmp2} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
if (task2.IsCompleted){
Console.WriteLine($"Function finished {idtmp2} {DateTime.Now.ToString("HH: mm:ss.ffff")}");
}else{
// async magic
}
Then you also need to consider what ExampleFunction will do before calling it's first await. If it's a CPU heavy task, it might take a long time to return. Does the second call block the current thread on a lock until the first call returns? If there is some lock synchronisation between tasks, does it matter which thread / task resumes first?
While it is possible for the first async method to complete in the order you are hoping for. Is it likely?
I hope this modified example makes things a bit more clear.
Notes:
The tasks are started before calling WhenAll
A task runs synchronously until the first await so it will not yield control before that (in the case of a fake task with no awaits it will never yield control)
All work of is done by the same thread until the awaits 'come back'.
var task1 = Test("A");
var task2 = Test("B");
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: Sleeping");
await Task.Delay(TimeSpan.FromMilliseconds(300));
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: Before WhenAll");
await Task.WhenAll(task1,task2).ConfigureAwait(false);
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: After WhenAll");
Console.ReadLine();
async Task Test(string s)
{
Console.WriteLine($" {Thread.CurrentThread.ManagedThreadId}: Started {s}");
Task task = Task.Delay(TimeSpan.FromMilliseconds(100));
Console.WriteLine($" {Thread.CurrentThread.ManagedThreadId}: After starting work {s}");
await task.ConfigureAwait(false);
Console.WriteLine($" {Thread.CurrentThread.ManagedThreadId}: Finished {s}");
}
This prints
1: Started A
1: After starting work A
1: Started B
1: After starting work B
1: Sleeping
5: Finished B
7: Finished A
5: Before WhenAll
5: After WhenAll
You want to have two Tasks that are Mutually Exclusive.
In the async programming world we have a synchronization primitive called the Mutex.
You want the server side to have an Async Mutex.
<PackageReference Include="Nito.AsyncEx.Coordination" Version="5.1.2" />
public class Server
{
private readonly AsyncLock _asyncMutext = new();
public async Task Foo()
{
using var _ = await _asyncMutext.LockAsync();
Console.WriteLine("Starting Foo");
await Task.Delay(1000);
Console.WriteLine("Finishing Foo");
}
public async Task Bar()
{
using var _ = await _asyncMutext.LockAsync();
Console.WriteLine("Starting Bar");
await Task.Delay(1000);
Console.WriteLine("Finishing Bar");
}
}
Just scope your Mutex appropriately for your use case.

Do something periodically while waiting for result of an GetAsync

I want to start an GetAsync or PostAsync and then in a loop do something and check for results.
req1 = client.GetAsync(url_1); // a time consuming request
do
{
//do something here
var req2= await client.GetAsync(url_2);
var result2 = await req2.Content.ReadAsStringAsync();
} while (!IsResultReady(req1)); // check if url_1 job is done and stop the loop
var result1 = await req1.Content.ReadAsStringAsync();
this example should give you what you need
async Task Main()
{
var mainTask = MyLongRunningTask();
// mainTask is already started without await
do
{
await DoSomethingElse();
} while (!mainTask.IsCompleted);
}
public async Task MyLongRunningTask()
{
Console.WriteLine("Long Running Task Started");
await Task.Delay(3000); // simulating client.GetAsync(url_1)
Console.WriteLine("Long Running Task Finished");
}
async Task DoSomethingElse()
{
Console.WriteLine("doing some other tasks");
await Task.Delay(1000);
}
output:
Long Running Task Started
doing some other tasks
doing some other tasks
doing some other tasks
Long Running Task Finished

Async flow having different execution times

I have the two APIs, which call the same services.
This version performs in the time that the at most expensive task takes.
public async Task<double> Do()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var t1 = _service.Do1();
var t2 = _service.Do2();
await t1;
await t2;
return sw.Elapsed.TotalMilliseconds;
}
While this one performs in sum of each task delay.
public async Task<double> Do()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
await _service.Do1();
await _service.Do2();
return sw.Elapsed.TotalMilliseconds;
}
internal async Task Do1() => await Task.Delay(5000);
internal async Task Do2() => await Task.Delay(2000);
Why is that, what is actually happening ?
Tasks return "hot", or already started. The await keyword quite literally means hold here and wait for the task to complete. As such, when you await each operation individually, it's calling Do1, waiting for that to finish, and then calling Do2 and waiting for that to finish.
However, when you just store them in variables, it calls Do1, and then it calls Do2, while Do1 is still running. Later when you await these two variables, the code will hold waiting for each to actually complete on by one, but they're already both running.
Long and short, it's the difference between running serially and in parallel. Asynchronous operations can be run parallel, but they are not parallel by nature: it's two different concepts.
var t1 = _service.Do1(); < --- your 1st task started
var t2 = _service.Do2(); < --- your 2nd task started
await t1; < --- 1st awaited but it already did at least part of its job so far
await t2; < --- 2nd task awaited but, if its light task, probably already done so nothing to await
vs
await _service.Do1(); < --- 1st task started and blocks 2nd from starting
await _service.Do2(); < --- 2nd starts only after 1st finished
Obviously, case 1 runs in parallel for some time while second one runs in series thus takes full time of both tasks summed.
In the first case both tasks will be started before you await one of them. So you need only the time max(time(task1), time(task2)).
In the second case you start the first task wait for it to finish and then start the second one, so your time will be time(task1) + time(task2)
Edit:
Also be aware of CPU-bound Tasks as Tasks will not automatically run parallel if executed on the same thread, e.g.
public static async Task<double> Do()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var t1 = Do1();
var t2 = Do2();
await t1;
await t2;
var time = sw.Elapsed.TotalMilliseconds;
Console.WriteLine("time="+time); // time=3002.3204
return time;
}
public static async Task Do1()
{
var t = Task.Delay(1000);
while(t.Status != TaskStatus.RanToCompletion) {}
await t;
}
public static async Task Do2()
{
var t = Task.Delay(2000);
while(t.Status != TaskStatus.RanToCompletion) {}
await t;
}
Try it online!

Execute list of async tasks

I have a list of async functions I want to execute in order. When I run the following code I get the output:
Task 1 before
Task 2 before
Finished tasks
Why are my async functions not being awaited correctly?
[Test]
public async Task AsyncTaskList()
{
var data = "I'm data";
var tasks = new List<Func<object, Task>>() {Task1, Task2};
tasks.ForEach(async task =>
{
await task(data);
});
Debug.WriteLine("Finished tasks");
}
private static async Task Task1(object data)
{
Debug.WriteLine("Task 1 before");
await Task.Delay(1000);
Debug.WriteLine("Task 1 after");
}
private static async Task Task2(object data)
{
Debug.WriteLine("Task 2 before");
await Task.Delay(1000);
Debug.WriteLine("Task 2 after");
}
Because the await inside your ForEach delegate actually completes after the method exits. Change it to an actual foreach loop and awaiting will work as expected.
ForEach has no specific handling for Func<Task> (few delegate-accepting methods in the Base Class Library do, and you should note that they will almost invariably return a Task themselves). ForEach will only run the synchronous portion of your lambda - and that is the portion preceding the first await of a Task which does not complete synchronously (which is Task.Delay in your case). This is why you're seeing the "before" messages popping up at the expected time. As soon as your delegate hits await Task.Delay, the the rest of your lambda is scheduled to run sometime in the future and ForEach moves on to the next item in the list. The scheduled task continuations will then run unobserved and complete later.

Categories