正确使用Async / Await的困惑

3

这是我在Windows Forms应用程序中使用异步/等待的实现

async Task<int> DoAysnc1()
{
    await Task.Delay(3000);
    return 3000;
}

async Task<int> DoAsync2()
{
    await Task.Delay(5000);
    return 5000;
}

private async void button1_Click(object sender, EventArgs e)
{
    this.textBox1.Text = "";
    var doAsync1 = DoAysnc1();
    var doAsync2 = DoAysnc2();
    var async1 = await doAsync1;
    var async2 = await doAsync2;
    this.textBox1.Text = $"{async1} & {async2}";
}

5秒后,文本框中的结果为“3000 & 5000”。

但是,当我将 button1_Click 修改为以下内容时:

private async void button1_Click(object sender, EventArgs e)
{
    this.textBox1.Text = "";
    var async1 = await DoAysnc1();
    var async2 = await DoAysnc2();
    this.textBox1.Text = $"{async1} & {async2}";
}

结果是一样的,但需要8秒钟。

为什么 button1_Click 的第二个版本会表现得像同步操作?


1
在第二种情况下,只有在DoAsync1()完成后才会运行DoAsync2()。这很清楚吧?请查看msdn上的一张漂亮图片,解释了async/await背后发生的事情,请仔细研究。 - Sinatr
当引用一个 Task 时,它开始执行!await 的意思是,当你完成后,在这里重新进入我的方法。 - Callum Linington
http://stackoverflow.com/documentation/c%23/48/async-await#t=201607251201374786908 - Neel
@Nicorus:第二个结果不是同步执行,而是串行执行。同步执行意味着“阻塞调用线程”;串行执行意味着“一个接一个地执行”。 - Stephen Cleary
3个回答

5
以下是差异的解释:
this.textBox1.Text = "";
var doAsync1 = DoAysnc1();         // <--- Run DoAsync1
var doAsync2 = DoAysnc2();         // <--- Run DoAsync2
var async1 = await doAsync1;       // <--- wait for DoAsync1 to finish
var async2 = await doAsync2;       //<--- wait for DoAsync2 to finish
this.textBox1.Text = $"{async1} & {async2}";

当:

this.textBox1.Text = "";
var async1 = await DoAysnc1();          // <-- Run DoAsync1 and wait for it to be done
var async2 = await DoAysnc2();          // <-- Run DoAsync2 and wait for it to be done
this.textBox1.Text = $"{async1} & {async2}";

在第一个版本中,两个任务同时运行。而在第二个版本中,直到第一个任务完成之前,你永远不会运行第二个任务。
我认为阅读这篇文章对你的知识将是一个巨大的加分项。

4

附上一些注释,以使实际发生的事情更加清晰:

// Starts DoAsync1 asynchronously
var doAsync1 = DoAysnc1();

// Starts DoAsync2 asynchronously
var doAsync2 = DoAysnc2();

// From now on, both task are executing

// Wait for DoAsync1 completion
var async1 = await doAsync1;

// Wait for DoAsync2 completion
var async2 = await doAsync2;

对于第二种情况:
// Starts DoAsync1 asynchronously, and wait for the task completion (3s)
var async1 = await DoAysnc1();

// Starts DoAsync2 asynchronously, and wait for the task completion (5s)
var async2 = await DoAysnc2();

2

DoAsync1DoAsync2返回可等待的Task对象。如果你一个接一个地等待它们,那么它们将依次执行(先等待第一个完成,然后等待第二个)。为了并行运行它们,你可以先创建Task对象,然后使用Task.WhenAll等待它们的结果。

private async void button1_Click(object sender, EventArgs e)
{
    this.textBox1.Text = "";

    var task1 = DoAysnc1();
    var task2 = DoAysnc2();
    await Task.WhenAll(task1, task2)

    this.textBox1.Text = $"{task1.Result} & {task2.Result}";
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接