使用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
)。 对于具有缓存的方法(如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; } }