【问题标题】:Deadlock using async & await使用异步和等待的死锁
【发布时间】:2013-09-05 15:01:43
【问题描述】:

我希望我的程序遵循这个调用堆栈/工作流程:

  1. dispatch()
  2. authorize()
  3. httpPost()

我的想法是 httpPost() 将是异步的,而其他 2 个方法保持非异步。但是,由于某种原因,除非我使 2 和 3 异步,否则它将无法工作。可能我还是有些误会。

据我了解,我可以:

  1. 在调用异步方法时使用await 关键字(这将暂停该方法并在异步方法完成后继续),或者
  2. 省略await关键字,而是调用异步方法返回值的Task.Result,这将阻塞直到结果可用。

以下是工作示例:

private int dispatch(string options)
{
    int res = authorize(options).Result;
    return res;
}

static async private Task<int> authorize(string options)
{
    string values = getValuesFromOptions(options);
    KeyValuePair<int, string> response = await httpPost(url, values);
    return 0;
}

public static async Task<KeyValuePair<int, string>> httpPost(string url, List<KeyValuePair<string, string>> parameters)
{
    var httpClient = new HttpClient(new HttpClientHandler());
    HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(parameters));
    int code = (int)response.StatusCode;
    response.EnsureSuccessStatusCode();

    string responseString = await response.Content.ReadAsStringAsync();
    return new KeyValuePair<int, string>(code, responseString);
}

这是工作示例:

private int dispatch(string options)
{
    int res = authorize(options).Result;
    return res;
}

static private int authorize(string options)
{
    string values = getValuesFromOptions(options);
    Task<KeyValuePair<int, string>> response = httpPost(url, values);
    doSomethingWith(response.Result);    // execution will hang here forever
    return 0;
}

public static async Task<KeyValuePair<int, string>> httpPost(string url, List<KeyValuePair<string, string>> parameters)
{
    var httpClient = new HttpClient(new HttpClientHandler());
    HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(parameters));
    int code = (int)response.StatusCode;
    response.EnsureSuccessStatusCode();

    string responseString = await response.Content.ReadAsStringAsync();
    return new KeyValuePair<int, string>(code, responseString);
}

我还尝试将所有 3 种方法都设为非异步,将 httpPost 中的 awaits 替换为 .Results,但随后它会永远挂在行上 HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)).Result;

谁能启发我并解释我的错误是什么?

【问题讨论】:

    标签: c# .net http async-await


    【解决方案1】:

    您有一个SynchronizationContext,当您await 时,该上下文将被捕获,以便继续可以在该上下文中运行。

    您正在启动一个异步任务,并安排一个后续在您的主上下文中稍后运行。

    然后,在异步操作完成之前,您的主上下文中有代码对异步操作进行阻塞等待。无法安排继续运行,因为上下文正忙于等待继续。经典的死锁。

    这就是为什么“一直异步”很重要的原因,就像您在第一个示例中所做的那样。

    有一些技巧可以解决第二个示例中的死锁,但这仍然不是您应该做的事情。异步的全部意义在于避免阻塞你的线程。如果你只是对任务进行阻塞等待,那么你就违背了异步的目的。要么让一切异步,要么不异步,除非你别无选择。

    【讨论】:

    • 要测试这是否是正确的解释,请取非工作代码并将每个“await X”更改为“await X.ConfigureAwait(false)”。如果解释是正确的,它现在应该可以工作了。另一个注意事项:如果您在 ASP.Net 或 UI 应用程序中运行,则您有一个 SynchronizationContext。如果是这样,请比较在独立控制台项目中运行相同的代码。
    • @servy:但是,我没有得到的是我的工作示例和非工作示例之间的实际区别。我知道你说await 安排一个延续,而.Result 只是简单的块(是这样吗?),但这并不意味着我需要一个无限的异步等待链,因为没有await 我会阻塞/死锁我的线?我的意思是第一个示例在int res = authorize(options).Result; 阻塞,但第二个示例在doSomethingWith(response.Result); 中阻塞,为什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-24
    • 1970-01-01
    • 2016-12-22
    • 2021-09-18
    相关资源
    最近更新 更多