wait 和async,await一起使用引发的死锁问题

在某个项目开发过程中,偶然间发现在UI线程中async,await,wait三者一起使用会引发一个必然性的死锁问题。

一个简单的实例,代码很简单,在界面上放置一个Button,并在Button的click事件中调用一个Async标记的异步线程Run并调用Task 的Wait方法,注意Run方法开启的线程中什么代码都没有执行,然而这个时候运行程序并点击Button按钮会直接导致界面假死,这是由于Run方法中开启的线程和主线程产生死锁导致。

    

我们可以分析下点击Button按钮之后代码的执行顺序:

1.触发Button_Click事件之后,会调用Run方法,主线程会开始执行Run方法中的代码。

2.在Run方法中主线程执行到await关键字,会兵分两路,主线程跳出Run方法并执行Wait方法,并开始等待Run方法中的线程执行完成,而await后面的Task中的代码会继续执行。

3.然而由于await关键字的机制,在UI线程中调用await关键字后,Task.Run后面的代码同步交给UI线程去执行,即使后面没有代码需要执行。然而这个时候的UI线程正在等待Run中的线程执行完成,这就造成了一个非常典型的死锁问题。

可以标记下代码的执行顺序:

同样的,当run方法返回的是带有返回值的线程,并且在UI线程中通过Result获取值,也会产生同样的死锁问题。

当然,在UI线程中为了保持UI的响应性,一般也很少会使用wait关键字来等待一个线程。只不过当你在开发一个类库给项目中的其他人员使用的时候,为了避免使用者使用不当,还是 建议在线程之后加上ConfigureAwait(false),将其配置为在UI线程不可等待。

1
2
3
await Task.Run(()=> {
 
           }).ConfigureAwait(false);

 

以上问题是在UI线程中产生的,那如果是在非UI线程中执行以上代码也会产生死锁么?答案是不会。

这是因为在非UI线程中,第四步的代码会从线程池中抓取空闲线程执行,而await的调用线程不可能是空闲线程,所以就不会产生于类似的死锁问题。

不过,无论是在UI线程还是非UI线程,都不建议把wait和await 、async混合使用。

一旦在代码中使用了await 、async,最好一直使用,一定要避免使用Task.WaitTask<T>.Result方法。

 

posted @   残生  阅读(520)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示