CAsync2 myc = new CAsync2();await myc.SomeEnd1(); await myc.SomeEnd2();public class CAsync2{ public async Task M1() { await Task.Delay(4000); Console.Write("M1"); } public Task M2() { Console.WriteLine("M2"); return Task.CompletedTask; } public async Task SomeEnd1() { await M1(); await M2(); } public async Task SomeEnd2() { var t1 = M1(); var t2 = M2(); await t1; await t2; }}
CAsync2 myc = new CAsync2();
await myc.SomeEnd1();
await myc.SomeEnd2();
public class CAsync2
{
public async Task M1()
await Task.Delay(4000);
Console.Write("M1");
}
public Task M2()
Console.WriteLine("M2");
return Task.CompletedTask;
public async Task SomeEnd1()
await M1();
await M2();
public async Task SomeEnd2()
var t1 = M1();
var t2 = M2();
await t1;
await t2;
What is the result and why?
Result:
M1M2M2M1
M1M2
M2
M1
Reason:In the SomeEnd1 method, the M1 task is awaited before the M2 task has been called.
Suggestion:In the SomeEnd2 method, both tasks are started before awaiting any to complete. Ideally you wouldn’t await each task individually, instead use await Task.WhenAll()… SomeEnd2 would be better like this:
public async Task SomeEnd2() { var tasks = new List<Task>(); tasks.Add(M1()); tasks.Add(M2()); await Task.WhenAll(tasks); }
var tasks = new List<Task>();
tasks.Add(M1());
tasks.Add(M2());
await Task.WhenAll(tasks);
The result will be the same, but this pattern will allow you to add tasks as needed without the need to await each individually.
Futher thoughts:If you needed the result of one asynchronous method before starting another method, you can kick off all non-dependent tasks and then await the task(s) need for the dependent task(s) and still await Task.WhenAll(tasks) before you need the results.
Try the code below, I have added more output lines with some formatting that may help illustrate things a bit.
CAsync2 myc = new CAsync2();await myc.SomeEnd1();Console.WriteLine();Console.WriteLine();await myc.SomeEnd2();Console.WriteLine();Console.WriteLine();await myc.SomeEnd3();public class CAsync2{ public async Task<string> M1() { Console.WriteLine(" M1 start"); var value = Path.GetRandomFileName(); await Task.Delay(4000); Console.WriteLine(" M1 complete"); return value; } public async Task M2() { Console.WriteLine(" M2 start"); await Task.Delay(200); Console.WriteLine(" M2 complete"); } public Task M3(string value) { Console.WriteLine(" M3 start"); Console.WriteLine($" M3 - value = \"{value}\""); Console.WriteLine(" M3 complete"); return Task.CompletedTask; } public async Task SomeEnd1() { Console.WriteLine("SomeEnd1 start"); await M1(); await M2(); Console.WriteLine("SomeEnd1 complete"); } public async Task SomeEnd2() { Console.WriteLine("SomeEnd2 start"); var tasks = new List<Task>(); tasks.Add(M1()); tasks.Add(M2()); await Task.WhenAll(tasks); Console.WriteLine("SomeEnd2 complete"); } public async Task SomeEnd3() { Console.WriteLine("SomeEnd3 start"); var tasks = new List<Task>(); var m1 = M1(); tasks.Add(m1); tasks.Add(M2()); var m1Result = await m1; tasks.Add(M3(m1Result)); await Task.WhenAll(tasks); Console.WriteLine("SomeEnd3 complete"); }}
Console.WriteLine();
await myc.SomeEnd3();
public async Task<string> M1()
Console.WriteLine(" M1 start");
var value = Path.GetRandomFileName();
Console.WriteLine(" M1 complete");
return value;
public async Task M2()
Console.WriteLine(" M2 start");
await Task.Delay(200);
Console.WriteLine(" M2 complete");
public Task M3(string value)
Console.WriteLine(" M3 start");
Console.WriteLine($" M3 - value = \"{value}\"");
Console.WriteLine(" M3 complete");
Console.WriteLine("SomeEnd1 start");
Console.WriteLine("SomeEnd1 complete");
Console.WriteLine("SomeEnd2 start");
Console.WriteLine("SomeEnd2 complete");
public async Task SomeEnd3()
Console.WriteLine("SomeEnd3 start");
var m1 = M1();
tasks.Add(m1);
var m1Result = await m1;
tasks.Add(M3(m1Result));
Console.WriteLine("SomeEnd3 complete");
In fact, you don’t need to add the m1 Task to the task list since we are waiting for it to be completed before startng M3.