zoukankan      html  css  js  c++  java
  • C# ConfigureWait

    ConfigureAwait

    参数为bool类型。true:尝试将延续任务封送回原始上下文

    我们一般使用的是false,用于避免强制在原始上下文或调度程序中进行回调。

    原理:

    以await DoSomeThingAsync().ConfigureAwait(false)为例。

    await等待返回后,需要获取上下文(比如UI)的线程,然后继续执行后续的代码。

    本来是回到UI线程去执行的,这里添加了ConfigureAwait(false)后,就从线程池中使用工作线程去执行后续代码了。

     1     private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
     2     {
     3         Debug.WriteLine($"ButtonBase_OnClick-start");
     4         await DoA();
     5         Debug.WriteLine($"ButtonBase_OnClick-end");
     6     }
     7     private async Task DoA()
     8     {
     9         Debug.WriteLine($"DoA-start");
    10         await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false);
    11         Debug.WriteLine($"DoA-end");
    12     }

    如上代码,输出调试信息位置的几处,所处线程分别是:

    ButtonBase_OnClick-start -- UI线程
    DoA-start -- UI线程
    DoA-end -- 工作线程
    ButtonBase_OnClick-end -- UI线程

    可以看出,DoA方法中调用ConfigureAwait(false)后后续代码可以在新的工作线程中执行,ButtonBase_OnClick不调用则会默认切换至UI线程继续执行后续代码。

    使用-提升性能

    减少了回调原始线程(比如UI线程),可以适当减少因排队回调次数过多造成的性能影响

    不必要回到原始线程,可以减少原始线程的处理。这也是多线程编程的一个实现方式。

    使用-避免死锁

    什么情况会死锁?

    当你基于某些原因(比如想将异步方法转同步、或者仅仅只是想等待完成),使用了.Wait()、.Result、GetAwaiter().GetResult()方法时,可能会出现死锁。

     1     private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
     2     {
     3         Debug.WriteLine($"ButtonBase_OnClick-start");
     4         DoA().Wait();
     5         Debug.WriteLine($"ButtonBase_OnClick-end");
     6     }
     7     private async Task DoA()
     8     {
     9         Debug.WriteLine($"DoA-start");
    10         await Task.Delay(TimeSpan.FromSeconds(5));
    11         Debug.WriteLine($"DoA-end");
    12     }

    如上,当使用了.Wait()后,在DoA方法中有任务等待,就会出现死锁。

    为什么会死锁?

    以上方法,是通过同步阻塞的方式来完成等待的。我们的UI上下文只有一个线程(并发数量是1),在调用以上方法后,子方法中await无法回调至UI线程(因为唯一的线程已经被阻塞了)。

    怎么解决死锁?

    在await DoSomeThingAsync()后添加.ConfigureAwait(false),那么它就不会将回调排队送回原始上下文,进而避免了死锁。

    其实就是假装没有上下文,然后默认让线程池线程处理。

    一些问题

    1.使用.ConfigureAwait(false)后,后续代码中有调用UI线程如控件,会有线程调度异常

    -- 建议在UI层,不使用.ConfigureAwait(false)。或者只对需要调用UI线程的代码,添加切换至UI线程的逻辑(如Dispatcher.InvokeAsync)

    2.添加.ConfigureAwait(false)后,后续的代码并没有预期的在工作线程执行。

    -- 这是可能的。因为await需要等待时,才会需要切换至原始上下文。当等待已经完成的Task时,后续代码将会保持同步运行,无需上下文的回调

    以上是个人总结后,相对简单、重要的部分。

    你也可以看看其它:

     

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Android-fragment-ListView展示-v4支持包
    Android-fragment的替换-V4支持包
    Android-fragment的替换
    Jenkins安装与使用(CentOS6.5)
    在tlog里统计注册统计相关功能
    通过t_log文件计算次日留存
    第九章练习
    练习
    python2.7安装完后,执行python时,出现import readline ImportError: No module named readline 以及tab补全
    (转)时间同步介绍
  • 原文地址:https://www.cnblogs.com/kybs0/p/14461501.html
Copyright © 2011-2022 走看看