死锁

2013-09-05 196 views
5

我想在我的程序,实现如下调用堆栈/工作流程:死锁

  1. 讯()
  2. 授权()
  3. httpPost()

我的想法是,httpPost()将是异步的,而另外两种方法仍然是非异步的。但是,由于某种原因,除非我做了2 + 3,否则它不适用于我。异步。也许我仍然有一些误解。

据我的理解,我可以a)在调用异步方法时使用await关键字(这将挂起方法并在异步方法完成后继续)或者b)省略关键字,而是调用Task。异步方法的结果返回值,该值将阻塞直到结果可用。


让我告诉你的工作示例:

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中的await.Result S,但随后它会永远在该行挂HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)).Result;

可能有人开导我,并说明我的错误是什么?

回答

11

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

您正在开始一个异步任务,计划在稍后的某个时间点在主要环境中运行连续任务。

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

这就是为什么“第一步异步”很重要,正如你在第一个例子中所做的那样。

在第二个例子中有一些可以解决死锁的黑客,但它仍然不是你应该做的。整个异步点是为了避免阻塞你的线程。如果你只是在阻止等待任务的时候进行,那么你就会破坏异步的目的。要么一切都是异步的,要么是不同步的,除非你没有选择。

+0

若要测试这是否正确的解释,采取非工作代码并将每个“await X”更改为“await X.ConfigureAwait(false)”。如果解释是正确的,它现在应该工作。 另一个注意事项:如果你在ASP.Net或UI应用程序中运行,你有一个SynchronizationContext。如果是这样,请比较在独立控制台项目中运行相同的代码。 – danarmak

+1

@servy:然而,我没有得到的是我的工作示例和非工作示例之间的实际区别。我明白你说'await'安排了一个延续,'.Result'只是普通块(是这样吗?),但这不会意味着我需要一个无尽的异步等待链,因为没有'await'我会阻止/死锁我的线程? 我的意思是第一个例子在'int res = authorize(options).Result;'中不*拦截,但第二个例子在'doSomethingWith(response.Result)中拦截;为什么? – user826955