使用 async & 的死锁等待 [英] Deadlock using async & await

查看:20
本文介绍了使用 async & 的死锁等待的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want my program to follow this callstack/workflow:

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

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

My idea was that httpPost() will be async, while the other 2 methods remain non-async. However, for some reason, it would not work unless I made 2 and 3 async. Maybe I still have some misunderstandings.

据我所知,我可以:

  1. 在调用异步方法时使用 await 关键字(这将挂起方法并在异步方法完成后继续),或
  2. 省略await 关键字,而是调用异步方法返回值的Task.Result,这将阻塞直到结果可用.
  1. use the await keyword when calling the async method (this will suspend the method and continue after the async method completes), or
  2. omit the await keyword and instead call Task.Result of the async methods return value, which will block until the result is available.

<小时>

这是工作示例:


Here is the working example:

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;

I also tried to have all 3 methods non-async, replacing the awaits in httpPost with .Results, but then it hangs forever on the line HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)).Result;

有人可以启发我并解释我的错误是什么吗?

Could someone enlighten me and explain what my mistake is?

推荐答案

您有一个 SynchronizationContext,并且在您 await 时正在捕获该上下文,以便继续(s) 可以在该上下文中运行.

You have a SynchronizationContext, and that context is being captured when you await so that the continuation(s) can run in that context.

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

You're starting an async task, scheduling a continutation to run in your main context at some later point.

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

Then, before the async operation is done, you have code in your main context doing a blocking wait on the async operation. The continuation cannot be scheduled to run because the context is busy waiting on the continuation. Classic deadlock.

这就是为什么一直向上异步"很重要,就像您在第一个示例中所做的那样.

This is why it's important to "async all the way up", as you did in your first example.

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

There are a few hacks that can work around the deadlock in the second example, but it's still not something you should be doing. The entire point of going asynchronous is to avoid blocking your thread(s). If you just go doing a blocking wait on the task anyway you're defeating the purpose of going asynchronous. Either make everything asynchronous, or nothing asynchronous, unless you don't have a choice.

这篇关于使用 async &amp; 的死锁等待的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆