简体   繁体   English

使用异步和等待的死锁

[英]Deadlock using async & await

I want my program to follow this callstack/workflow:我希望我的程序遵循此调用堆栈/工作流程:

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

My idea was that httpPost() will be async, while the other 2 methods remain non-async.我的想法是httpPost()将是异步的,而其他 2 个方法保持非异步。 However, for some reason, it would not work unless I made 2 and 3 async.但是,出于某种原因,除非我使 2 和 3 异步,否则它不会起作用。 Maybe I still have some misunderstandings.可能我还有一些误会。

To my understanding I can either:据我了解,我可以:

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

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);
}

Here is the non -working example:是非工作示例:

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);
}

I also tried to have all 3 methods non-async, replacing the await s in httpPost with .Result s, but then it hangs forever on the line HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)).Result;我还试图将所有3种方法的非异步,替换await以s httpPost.Result S,但随后就行永远挂起HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)).Result;

Could someone enlighten me and explain what my mistake is?有人可以启发我并解释我的错误是什么吗?

You have a SynchronizationContext , and that context is being captured when you await so that the continuation(s) can run in that context.您有一个SynchronizationContext ,并且在您await时会捕获该上下文,以便延续可以在该上下文中运行。

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.除非您别无选择,否则要么使一切异步,要么不异步。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM