zoukankan      html  css  js  c++  java
  • 【C#】await & Result DeadLock

    随意使用异步的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是一个同步等待

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

  • 相关阅读:
    js返回到顶部
    css培训一
    css常用hack技巧
    css培训二
    css样式渲染规则
    html语义(一)
    css样式表管理
    html+css培训方案
    继承
    封装
  • 原文地址:https://www.cnblogs.com/catzhou/p/10727546.html
Copyright © 2011-2022 走看看