浅谈C# async await 死锁问题总结_C#教程

来源:脚本之家  责任编辑:小易  

其实任2113何程序都是需要内存的,就像5261电脑为4102啥要有内存条一样1653。所有程序都是回要通过CPU进行运算,一个CPU到时答有寄存器,但CPU是用于计算的,寄存器不可能很大。那么程序只能放内存了。只是C语言的内存可以通过开发人员咨询分配释放,因此能够更灵活分配内存。当然因为是人为分配内存,也会更容易出现内存破坏问题。栈可以理解为程序放到内存的一个区域,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等www.zgxue.com防采集请勿采集本网。

可能发生死锁的程序类型

结构体里面包含两个变量,那个数组占4个自己,float变量占4个字节,那个kitty就是一个结构体变量,sizeof计算的是结构体所占字节数,数组c跟b变量各位4个字节,就是8了。

1、WPF/WinForm程序

浅谈数控机床C轴的功能和控制摘要:C轴越来越广泛地被应用到数控机床上,根据不同品牌的机床,它的功能与控制都有所不同,但基本原理没变,还是360度的一个圆作为一个可控制轴。这里浅显地介绍一下C轴定义和功能,以及它的驱动和检测,结合三菱

2、asp.net (不包括asp.net core)程序

1、抓好第一堂课,明确学习目的。 2、鼓励参加考试,增加学习动力。 3、营造课堂氛围,激发学习兴趣。 4、运用问题教学法,激发学习主动性。

死锁的产生原理

C语言效率高; 操作系统的API本身就是C封装,参数无需转换就可以调用; C语言的程序员多; 。。。

对异步方法返回的Task调用Wait()或访问Result属性时,可能会产生死锁。

include #include int jc(int m){ if(m!=1) return m*jc(m-1); else return 1; } int c(int m,int n){ if(m>=n) return jc(m)/(jc(n)*jc(m-n)); } int main(void){ int m,n; scanf("%d%d",&m,&n); printf("%d\n",c(m,n)); return 0; }

下面的WPF代码会出现死锁:

private void Button_Click_7(object sender, RoutedEventArgs e) { Method1().Wait(); } private async Task Method1() { await Task.Delay(100); txtLog.AppendText("后续代码"); }

下面的asp.net mvc代码也会出现死锁:

public ActionResult Index() { string s=Method1().Result; return View(); } private async Task<string> Method1() { await Task.Delay(100); return "hello"; }

以WPF代码为例,事件处理器调用Method1,得到Task对象,然后调用Task的Wait方法,阻塞自己所在的线程,即主线程,直到Task对象“完成”。而返回的Task对象要想“完成”,必须在主线程上执行await之后的代码。而主线程早就处于阻塞状态,它在等待Task对象完成!于是死锁就产生了。

asp.net mvc代码是同样的道理。

什么时候必然会死锁,如何避免

从上面的两个例子中似乎可以得出结论:在WPF/WinForm/asp.net程序中,在异步方法上调用.Result/Wait(),就会产生死锁。然而事实并非如此。

如下面的WPF代码就不会出现死锁:(从web获取数据并显示在文本框中。此代码仅为举例说明,异步事件处理器才是正道)

private void Button_Click_8(object sender, RoutedEventArgs e) { HttpClient httpClient = new HttpClient(); httpClient.BaseAddress = new Uri("https://www.baidu.com/"); string html = httpClient.GetStringAsync("/").Result; html = "【" + html + "】"; txtLog.AppendText(html); }

把获取数据的代码摘出来吧:

private void Button_Click_8(object sender, RoutedEventArgs e) { string html = GetHtml(); txtLog.AppendText(html); } private string GetHtml() { HttpClient httpClient = new HttpClient(); httpClient.BaseAddress = new Uri("https://www.baidu.com/"); string html=httpClient.GetStringAsync("/").Result;       html = "【" + html + "】";        return html; }

完全没问题,这是肯定的。

GetHtml()可以写成异步方法,再改一下:

private void Button_Click_8(object sender, RoutedEventArgs e) { string html = GetHtml().Result; txtLog.AppendText(html); } private async Task<string> GetHtml() { HttpClient httpClient = new HttpClient(); httpClient.BaseAddress = new Uri("https://www.baidu.com/"); string html=await httpClient.GetStringAsync("/");               html = "【" + html + "】";  return html; }

(HttpClient的GetStringAsync()方法是异步方法,我们调用它,然后用async/await的方式创建了一个自己的异步方法。先不“一路异步到底(Async All the Way)”。)

运行一下,死锁出现了。

为什么在HttpClient的GetStringAsync()方法上执行.Result不会死锁,而在自己写的异步方法上执行.Result,就出现了死锁?难道HttpClient的GetStringAsync()方法内部有什么特殊的处理?

看一下mono的HttpClient源代码,可以发现:

所有await 表达式后面,都加了ConfigureAwait (false),如

return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);

而由Task的msdn文档可以知,ConfigureAwait (false)会指示await之后的代码不在原先的context (可理解为线程)上运行。

修改一下GetHtml()异步方法的代码:

private void Button_Click_8(object sender, RoutedEventArgs e) { string html = GetHtml().Result; txtLog.AppendText(html); } private async Task<string> GetHtml() { HttpClient httpClient = new HttpClient(); httpClient.BaseAddress = new Uri("https://www.baidu.com/"); string html=await httpClient.GetStringAsync("/").ConfigureAwait(false);               html = "【" + html + "】";  return html; }

可以发现,死锁不会出现了。

分析:GetHtml()被调用后,主线程阻塞,等待Task对象“完成”;HttpClient获取数据完毕,在另外的线程上执行了await的之后的代码,于是Task对象完成。主线程恢复执行。(注意,即使“await之后没有代码”,即GetHtml()方法体中直接写return await httpClient.GetStringAsync("/"),也是需要加.ConfigureAwait(false)的)

当然,如果事件处理器是异步的,即使不加.ConfigureAwait(false),也不会有任何问题:

private async void Button_Click_8(object sender, RoutedEventArgs e) { string html = await GetHtml(); txtLog.AppendText(html); } private async Task<string> GetHtml() { HttpClient httpClient = new HttpClient(); httpClient.BaseAddress = new Uri("https://www.baidu.com/"); string html = await httpClient.GetStringAsync("/"); html = "【" + html + "】"; return html; }

试想一下,如果GetHtml()被放到单独的类中,做成类库,那么,里面如果不加.ConfigureAwait(false),则只能假设使用这个类库的人严格遵循异步编程规范了。一旦使用者在GetHtml()上执行.Result,死锁就无可避免了。

仔细看HttpClient的源代码,可以发现,它的GetStringAsync()方法也并不是“天生的”异步方法,它也是用await运算符调用了自己的其他的异步方法,并且在每次调用后都添加了.ConfigureAwait(false)。

那么,最初的WPF程序的死锁是否可以用.ConfigureAwait(false)解决呢?注意,txtLog是一个文本框,UI控件只能在UI线程访问,所以添加上.ConfigureAwait(false)后会报错:“InvalidOperationException: 调用线程无法访问此对象,因为另一个线程拥有该对象”。那么是否可以改成这样:

private void Button_Click_7(object sender, RoutedEventArgs e) { Method1().Wait(); } private async Task Method1() { await Task.Delay(100).ConfigureAwait(false); Dispatcher.Invoke(() => { txtLog.AppendText("后续代码"); }); }

依然是死锁。所以,乖乖的用异步事件处理器吧:

private async void Button_Click_7(object sender, RoutedEventArgs e) { await Method1(); } private async Task Method1() { await Task.Delay(100); txtLog.AppendText("后续代码"); }

上面的代码还说明一个问题:在异步工具方法中,不要写访问UI控件的代码,否则无法规避死锁问题。

总结 死锁会发生在不遵循异步编程规范——在异步方法返回的Task对象上执行Wait()或.Result时 ConfigureAwait(false)指定await后的代码不返回原先的context,可以避免死锁

如果await之后的代码不需要返回原先的context执行,例如,仅仅是执行Http请求,获取和处理数据,那么完全可以加上ConfigureAwait(false)。 如果作为类库的创作者,编写异步方法时,应尽可能的使用ConfigureAwait(false),以保证一旦类库的使用者阻塞异步方法时,不会产生死锁。

在异步类库/工具方法中,应避免加入访问UI控件的代码 

附加  async/await学习资料

 C# Under the Hood: async/await  作者从动手写一个“可等待”的方法开始,进而通过反编译工具分析异步方法生成的的实质代码,揭示了async/await的本质——回调

What happens in an async method  msdn编程指南,图示异步方法的执行流程

到此这篇关于浅谈C# async await 死锁问题总结的文章就介绍到这了,更多相关C# async await 死锁内容请搜索真格学网以前的文章或继续浏览下面的相关文章希望大家以后多多支持真格学网! 您可能感兴趣的文章:一文搞懂c# await,async执行流C#关键字async/await用法浅谈C#中的Async和Await的用法详解说说C#的async和await的具体用法详解C#中 Thread,Task,Async/Await,IAsyncResult的那些事儿详解C#中的Async和Await用法

  一、堆栈2113空间分配区别:  1、栈(操5261作系统4102):由操作系统自动分配释放 ,存放1653函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;  2、堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。  二、堆栈缓存方式区别:  1、栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;  2、堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。  三、堆栈数据结构区别:  堆(数据结构):堆可以被看成是一棵树,如:堆排序;  栈(数据结构):一种先进后出的数据结构内容来自www.zgxue.com请勿采集。


  • 本文相关:
  • c#连接到sql server2008数据库的实例代码
  • c#使用file.copy实现文件备份示例
  • c#检测上传文件真正类型的方法
  • wpf如何绘制光滑连续贝塞尔曲线示例代码
  • c#资源释放方法实例分析
  • c#使用ado.net读取excel表的方法
  • c#中params的用法
  • c#线程入门教程之单线程介绍
  • c# 后台处理图片的几种方法
  • c# 获取网页中指定的字符串信息的实例代码
  • 浅析C语言中堆和栈的区别
  • 浅谈c语言程序为什么需要内存 栈又是什么
  • 浅析C语言和Java语言的异同
  • 浅析c语言和java语言的异同
  • 浅谈如何理解C语言中的运算符sizeof
  • 浅谈数控机床C轴的功能和控制
  • 浅谈如何培养学生对C语言的学习兴趣
  • 浅谈为什么用C语言开发引擎
  • 浅析用c语言编程求解线性代数中行列式值
  • 浅析肖邦升c小调《幻想即兴曲》及其弹奏
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全c#教程vbvb.netc 语言java编程delphijavaandroidiosswiftscala易语言汇编语言其它相关首页一文搞懂c# await,async执行流c#关键字async/await用法浅谈c#中的async和await的用法详解说说c#的async和await的具体用法详解c#中 thread,task,async/await,iasyncresult的那些事儿详解c#中的async和await用法c#连接到sql server2008数据库的实例代码c#使用file.copy实现文件备份示例c#检测上传文件真正类型的方法wpf如何绘制光滑连续贝塞尔曲线示例代码c#资源释放方法实例分析c#使用ado.net读取excel表的方法c#中params的用法c#线程入门教程之单线程介绍c# 后台处理图片的几种方法c# 获取网页中指定的字符串信息的实例代码c#几种截取字符串的方法小结c#中httpwebrequest的用法详解c# datagridview添加新行的2个方c#中list〈string〉和string[]数c# 一个wcf简单实例c#连接mysql数据库的方法c#的dllimport使用方法详解c#中使用split分割字符串的几种方c#处理3种json数据的实例c#实现16进制和字符串之间转换的c#中38个常用运算符的优先级的划分和理解c#根据网址抓取网页截屏生成图片的示例c#使用mailaddress类发送html格式邮件的实c#打包文件解压缩的实例c#实现托盘程序并禁止多个应用实例运行的c#用combobox控件实现省与市的联动效果的c#:(变量)字段和局部变量的作用域冲突c# socket 发送&接收&返回 简单应用实例c#发送邮箱实现代码c#实现读取dataset数据并显示在listview控
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved