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
这样就貌似就不会出现死锁了
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中的异常的,如果有什么未处理的异常,可能会影响系统的稳定性