欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【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

上面两个例子的运行过程是:

  1. 在ui/asp.net context,调用getjsonasync方法;
  2. 在ui/asp.net context,getjsonasync方法调用httpclient.getstringasync开始一个rest请求;
  3. getstringasync返回一个未完成的task,表示rest请求没有完成;
  4. getjsonasync等待getstringasync返回的task。当前context被捕获(保存),当前context在getjsonasync完成时将被调用。getjsonasync返回一个未完成的task,表示getjsonasync方法未完成;
  5. jsontask.result同步阻塞getjsonasync返回的任务,即阻塞context;
  6. ...然后,rest请求完成了,然后通知getstringasync方法;
  7. getstringasync准备继续任务,他等待context可用,然后他可以在context运行;
  8. 死锁!jsontask.result阻塞了context线程,等待getstringasync完成,getstringasync等待context空闲,然后它可以完成。

防止死锁

两点经验:

  1. 异步方法中,尽可能添加configureawait(false)
  2. 别阻塞;使用 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是一个同步等待

同步等待在控制台程序、单元测试中不会死锁