使用HttpClient避免死锁

使用HttpClient并避免死锁的最佳方法是什么? 我正在使用下面的代码,完全来自同步方法,但我担心它可能导致死锁。

我已经完成了对.GetAwaiter() .ConfigureAwait(false) .GetAwaiter() .GetResult()等函数的一些阅读,但我正在寻找关于最佳实践方法的输入。

代码不太准确,但足够接近。

 public static bool TryRequest(string url, out response) { HttpContent content = new StringContent(json, Encoding.UTF8, "application/json"); using (HttpClient client = new HttpClient()) { HttpResponseMessage responseMessage = null; switch (verb) { case HttpVerb.Put: responseMessage = client.PutAsync(url, content).Result; break; case HttpVerb.Post: responseMessage = client.PostAsync(url, content).Result; break; case HttpVerb.Delete: responseMessage = client.DeleteAsync(url).Result; break; case HttpVerb.Get: responseMessage = client.GetAsync(url).Result; break; } if (responseMessage.IsSuccessStatusCode) { responseContent = responseMessage.Content.ReadAsStringAsync().Result; statusCode = responseMessage.StatusCode; } } } 

看起来您正在尝试同步运行异步代码。

使用WinForms和WPF, 你不能安全地做到这一点!

你唯一能做的就是一直使用异步。 它是.net异步的已知问题。 您可以使用public async void XXX()方法。 但是你不知道他们何时完成。 在与事件处理程序结合使用时,您应该只使用async void

您遇到死锁的原因是默认的TaskFactory会尝试将中断回调TaskFactory送回SynchronizationContext ,这可能是您的UI线程。

即使您使用Task.ConfigureAwait(false) ,也无法保证在callstack下面没有一个需要UI线程的回调。

只要阻止SynchronizationContext线程,就很有可能会死锁。

值得注意的是,异步代码有时可能有效。 这是因为,允许同步完成返回Task的异步方法(例如Task.Return(T result) )。 对于具有缓存的方法(如HttpRequests),通常会发生这种情况。

编辑:@SriramSakthivel建议您可以通过将其包装在Task.Run同步运行异步方法。 这是因为Task.Run将运行没有父SynchronizationContext的代码。

Task.Run(RunRequest).Result;

我个人不推荐这个,因为它依赖于Task.Run的特定实现以及TaskFactory来工作。 完全可能(但不太可能)新版本的.net将破坏这段代码。

我认为你的意思是避免阻止。 死锁指的是两个或多个线程都无限期地等待彼此完成的情况。

要避免在示例代码中阻塞,而不是同步等待Results,您只需要await非阻塞API调用:

 HttpContent content = new StringContent(json, Encoding.UTF8, "application/json"); using (HttpClient client = new HttpClient()) { HttpResponseMessage responseMessage = null; switch (verb) { case HttpVerb.Put: responseMessage = await client.PutAsync(url, content); break; case HttpVerb.Post: responseMessage = await client.PostAsync(url, content); break; case HttpVerb.Delete: responseMessage = await client.DeleteAsync(url); break; case HttpVerb.Get: responseMessage = await client.GetAsync(url); break; } if (responseMessage.IsSuccessStatusCode) { responseContent = await responseMessage.Content.ReadAsStringAsync(); statusCode = responseMessage.StatusCode; } }