【C#】await & Result DeadLock
程序员文章站
2022-06-18 09:58:41
随意使用异步的await和Result,被弄得欲仙欲死,然后看了 "Don't Block on Async Code" ,稍许明白,翻译然后加上自己的理解以加深印象。 会死锁的两个例子 UI例子 public static async Task GetJsonAsync(Uri uri) { us ......
随意使用异步的await和result,被弄得欲仙欲死,然后看了 don't block on async code,稍许明白,翻译然后加上自己的理解以加深印象。
会死锁的两个例子
ui例子
public static async task<jobject> getjsonasync(uri uri) { using (var client = new httpclient()) { var jsonstring = await client.getstringasync(uri); return jobject.parse(jsonstring); } } // my "top-level" method. public void button1_click(...) { var jsontask = getjsonasync(...); textbox1.text = jsontask.result; }
** asp.net例子**
public static async task<jobject> getjsonasync(uri uri) { using (var client = new httpclient()) { var jsonstring = await client.getstringasync(uri); return jobject.parse(jsonstring); } } // my "top-level" method. public class mycontroller : apicontroller { public string get() { var jsontask = getjsonasync(...); return jsontask.result.tostring(); } }
死锁的原因
await 一个task后,当task完成后将继续一个context。
ui例子的content是 ui content,asp.net例子的content是request content。在任何时候,这两个content只能属于一个线程,是不能被具体的线程捆绑(tied)。这个有趣或者恶心的特色没被官方文档说明,只在my msdn article about synchronizationcontext。
上面两个例子的运行过程是:
- 在ui/asp.net context,调用getjsonasync方法;
- 在ui/asp.net context,getjsonasync方法调用httpclient.getstringasync开始一个rest请求;
- getstringasync返回一个未完成的task,表示rest请求没有完成;
- getjsonasync等待getstringasync返回的task。当前context被捕获(保存),当前context在getjsonasync完成时将被调用。getjsonasync返回一个未完成的task,表示getjsonasync方法未完成;
- jsontask.result同步阻塞getjsonasync返回的任务,即阻塞context;
- ...然后,rest请求完成了,然后通知getstringasync方法;
- getstringasync准备继续任务,他等待context可用,然后他可以在context运行;
- 死锁!jsontask.result阻塞了context线程,等待getstringasync完成,getstringasync等待context空闲,然后它可以完成。
防止死锁
两点经验:
- 异步方法中,尽可能添加configureawait(false) ;
- 别阻塞;使用 async
根据第一点经验:
var jsonstring = await client.getstringasync(uri);
改成
var jsonstring = await client.getstringasync(uri).configureawait(false);
根据第二点经验,调用异步方法的代码如下:
public async void button1_click(...) { var json = await getjsonasync(...); textbox1.text = json; } public class mycontroller : apicontroller { public async task<string> get() { var json = await getjsonasync(...); return json.tostring(); } }
await 是一个异步等待
.result是一个同步等待
同步等待在控制台程序、单元测试中不会死锁
下一篇: 事件Event一