简体   繁体   中英

what happens if I await a task that is already running or ran?

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.

I threw together this quick test:

var i = 0;
var task = Task.Run(() => { i++; Console.WriteLine(i); return i; });

var x = await task;
var y = await task;

Console.WriteLine(x);
Console.WriteLine(y);

It writes:

1
1
1

So, clearly, the task only runs once.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM