C# 通过TaskCompletionSource解决async/await死锁的问题
async/await 是个好东西
但是呢,使用async/await时,很多旧项目经常会出现最外层要同步调用async/await方法的问题
而当我们在asp.net程序和winform等具有UI线程的项目使用async/await的Result属性或者Wait()方法同步阻塞获取数据时
就会出现死锁,具体的原因网上一搜一大把,这里就不做具体的介绍了
大家一般通过给await函数添加.ConfigureAwait(false)来解决这个问题
但是如果咱们引用的第三方dll内部没有这个.ConfigureAwait(false)或者其他未知问题,那就悲剧了,即使你给你所有的await都加上.ConfigureAwait(false)也依然是死锁
这个时候就只能另想办法了,而通过TaskCompletionSource就能解决这个问题,虽然很绕,很诡异,但是至少是解决死锁了。。。
具体的思路是创建一个TaskCompletionSource泛型对象,泛型类型为async/await返回值类型,然后开启一个新的异步线程,在这个异步线程中执行async/await方法,并将await的结果赋值给TaskCompletionSource,最后在异步线程的外面,同步阻塞调用TaskCompletionSource对象Task.Result
这样就貌似就不会出现死锁了
1 2 3 4 5 6 7 8 | public static T Result<T>(Func<Task<T>> func) { var tcs = new TaskCompletionSource<T>(); Task.Run(async () => { T data = await func().ConfigureAwait( false ); tcs.SetResult(data); }); return tcs.Task.Result; } |
具体是怎么解决的,我也不是很清楚,估计还是线程竞争的问题,咱们启用了一个新的异步线程,估计是避免了这种竞争,还需要好好研究下
还有就是要注意Task.Run中func函数的异常处理,由于是异步线程,外部是捕获不到Task.Run中的异常的,如果有什么未处理的异常,可能会影响系统的稳定性
2023-10-10模拟测试,WinForm代码
public static T Result<T>(Func<Task<T>> func) { var tcs = new TaskCompletionSource<T>(); Task.Run(async () => { T data = await func().ConfigureAwait(false); tcs.SetResult(data); }); return tcs.Task.Result; } private void BtnTest2_Click(object sender, EventArgs e) { //MessageBox.Show(CCC().Result); //死锁 MessageBox.Show(Result(CCC)); //无法更改底层,使用TaskCompletionSource解决死锁 } /// <summary> /// 模拟层层调用 /// </summary> async Task<string> CCC() { return await BBB().ConfigureAwait(false); //其他层级都有ConfigureAwait(false) } /// <summary> /// 模拟层层调用 /// </summary> async Task<string> BBB() { return await AAA().ConfigureAwait(false); } async Task<string> AAA() { await Task.Delay(100); //.ConfigureAwait(false); //设定这里缺少ConfigureAwait(false) ,如果不缺少,外部直接Result就不会死锁了 return "执行"; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)