如何正确理解.NET 4.5和C#5.0中的async/await异步编程模式

 我来答
翲燚
推荐于2017-11-22 · 知道合伙人软件行家
翲燚
知道合伙人软件行家
采纳数:436 获赞数:3030

向TA提问 私信TA
展开全部

相对于之前Begin/End模式和事件模式,async/await模式让程序员得以用同步的代码结构进行异步编程。async/await入门很方便,但是深入理解却涉及很多领域,如线程池、同步上下文等等。我断断续续接触了几个月,稍微有一些心得:


await的作用是等待异步Task完成,并不是阻塞的。举个例子,一个异步方法:

public async Task Caller()
{
    Action0();
    await Method();
    Action3();
}

public async Task Method()
{
    Action1();
    await Task.Delay(1000);
    Action2();
}

A. 当你在非UI线程A上执行Caller(),将完成以下几件事:

  1. [线程A]执行Action0()

  2. [线程A]调用await Method()

  3. [线程A]执行Action1()

  4. [线程A]启动任务Task.Delay(1000),并在线程池里安插一个新任务,在Task.Delay(1000)完成后,由另一个线程执行6

  5. [线程A]去处理别的事情

  6. [线程B]执行Action2()

  7. [线程B]await Method()返回

  8. [线程B]执行Action3()

    其中,线程A和线程B并不保证是同一个线程。如果你在await前后打印线程ID,你会发现ID是不同的。


B. 当你在UI线程上执行Caller(),过程有了变化:

  1. [UI线程]执行Action0()

  2. [UI线程]调用await Method()

  3. [UI线程]执行Action1()

  4. [UI线程]启动任务Task.Delay(1000),并在线程池里安插一个新任务,在Task.Delay(1000)完成后,由另一个线程执行6

  5. [UI线程]去处理别的事情

  6. [线程C]在UI线程的同步上下文中执行7(类似于在窗体类上执行Invoke()方法)

  7. [UI线程]执行Action2()

  8. [UI线程]await Method()返回

  9. [UI线程]执行Action3()

    可见,当使用await方法的线程为UI线程时,程序默认会通过第6步,保证await前后是同一个线程ID。这个当然是有一定性能牺牲的(甚至会造成死锁,在D里会讨论),如果你不想在await完成后回到UI线程,见C。


C. 你可以在UI线程上使用await XXX().ConfigureAwait(false)去替代await XXX(),来禁止当await XXX()结束时恢复线程。举个例子,执行下列代码是没问题的(如B里描述的):

private async void button1_Click(object sender, EventArgs e)
{
    this.Text = "123";
    await Task.Delay(1000);
    this.Text = "321";
}

但是,执行下列代码就会发生“线程间操作无效”的错误:

private async void button1_Click(object sender, EventArgs e)
{
    this.Text = "123";
    await Task.Delay(1000).ConfitureAwait(false);
    this.Text = "321";    //线程间操作无效
}

因为执行

this.Text = "321";

的线程已经不再是UI线程。


D. 顺便一提,Task.Wait()方法,相比于await Task,会同步地执行Task。但是,如果你在UI线程上Wait的Task里本身又有await,那么将会产生死锁:

private void Foo(object sender, EventArgs e)
{
    this.Text = "123";
    Method().Wait();        //此处发生死锁
    this.Text = "321";    //这行永远也不会执行
}
private async Task Method()
{
    await Task.Delay(1000);
}

    为什么呢?Method().Wait()会阻塞UI线程等待Method()完成,但是参照B过程,在await完成后,Method()完成前,是需要恢复到UI线程的,但是此时UI线程已经被阻塞了,因此死锁就发生了。

    要避免这个死锁,可以参照C。


E. 说出来你可能不信,上面的都是我手打的。在内容上虽然不一定严谨,但希望对楼主和其它新接触TAP的朋友有一定启发。

推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式