如果我等待已经运行或运行的任务会发生什么?

Bil*_*ani 16 .net c# parallel-processing asynchronous task-parallel-library

有一个Task变量,让我们说现在正在运行任务..通过执行以下行.

await _task;
Run Code Online (Sandbox Code Playgroud)

我想知道当我编写这段代码时会发生什么:

await _task;
await _task;
Run Code Online (Sandbox Code Playgroud)

它会执行两次任务吗?或抛出异常,因为它已经运行?

Yuv*_*kov 15

它会执行两次任务吗?或抛出异常,因为它已经运行?

不,不.唯一await能做的就是调用Task.GetAwaiter,它不会导致任何运行.如果任务已经完成,它将提取值(如果它是a)Task<T>,或者如果它是a ,则同步运行到下一行Task,因为已完成任务的优化.

一个简单的演示:

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;
}
Run Code Online (Sandbox Code Playgroud)

如果您深入了解状态机本身,您会看到:

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;
}
Run Code Online (Sandbox Code Playgroud)

此优化首先检查任务的完成情况.如果任务未完成,它将调用UnsafeOnCompleted哪个将注册继续.如果它完成了,它会打破switch并转到:

this.<>1__state = -2;
this.<>t__builder.SetResult();
Run Code Online (Sandbox Code Playgroud)

哪个设置底层的结果Task,这样你实际上同步获得值.

  • 应该指出的是*这是一件好事*.关于异步代码的一个棘手的部分是大多数异步操作实际上可以同步完成 - 这是你必须处理的一个单独的情况,因为在这种情况下你的异步回调是*not*.`await`隐藏了所有这些复杂性,它允许你在十个不同的地方"等待"相同的任务(对于例如异步延迟初始化非常有用). (4认同)
  • @BilalFazlani - 您是否知道有一个特定的类旨在帮助延迟初始化? - [`Lazy <T>`](https://msdn.microsoft.com/en-us/library/dd642331(v = vs.110).aspx) (2认同)

Eni*_*ity 8

我把这个快速测试拼凑在一起:

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);
Run Code Online (Sandbox Code Playgroud)

它写道:

1
1
1
Run Code Online (Sandbox Code Playgroud)

所以,显然,任务只运行一次.