There is a Task variable and lets say the task is running right now.. by executing the following line.
await _task;
I was wondering what happens when I write this code:
await _task; await _task;
would it execute the task twice ? Or throw an exception because it has already run ?
would it execute the task twice ? Or throw an exception because it has already run ?
No and no. The only thing await
does is call Task.GetAwaiter
, it does not cause anything to run. If the task already ran to completion, it will either extract the value if it is a Task<T>
, or run synchronously to the next line if it is a Task
, since there is an optimization for already completed tasks.
A simple demo:
async Task Main() { var foo = FooAsync(); await foo; await foo; var bar = BarAsync(); var firstResult = await bar; var secondResult = await bar; Console.WriteLine(firstResult); Console.WriteLine(secondResult); } public async Task FooAsync() { await Task.Delay(1); } public async Task<int> BarAsync() { await Task.Delay(1); return 1; }
If you drill down to the state machine itself, you'll see this:
this.<foo>5__1 = this.<>4__this.FooAsync(); taskAwaiter = this.<foo>5__1.GetAwaiter(); if (!taskAwaiter.IsCompleted) { this.<>1__state = 0; this.<>u__1 = taskAwaiter; M.<FooBar>d__0 <FooBar>d__ = this; this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, M.<FooBar>d__0> (ref taskAwaiter, ref <FooBar>d__); return; }
This optimization first checks the completion of the task. If the task isn't complete, it will call UnsafeOnCompleted
which will register the continuation. If it is complete, it breaks the switch
and goes to:
this.<>1__state = -2; this.<>t__builder.SetResult();
Which sets the result for the underlying Task
, and that way you actually get the value synchronously.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With