为什么HttpClient似乎在这里死锁?

我有一个在便携式类库中制作的API,需要使用特定于平台的API来发送HTTP请求。 这是我写的在WinRT上执行HTTP POST的方法:

public bool Post(IEnumerable<KeyValuePair> headers, string data) { bool success = false; HttpClient client = new HttpClient(new HttpClientHandler {AllowAutoRedirect = false}); foreach (var header in headers) { client.DefaultRequestHeaders.Add(header.Key, header.Value); } try { var task=client.PostAsync(endpoint, new StringContent(data, Encoding.UTF8, "text/xml")).ContinueWith( postTask => { try { postTask.Wait(client.Timeout); //Don't wait longer than the client timeout. success = postTask.Result.IsSuccessStatusCode; }catch {} }, TaskContinuationOptions.LongRunning); task.ConfigureAwait(false); task.Wait(client.Timeout); } catch { success = false; } return success; } 

尽管存在任何压力,但这表现出一个有趣的问题。 它似乎在内部陷入僵局。 就像我创建5个线程并从它们发送POST请求一样,这个方法将到达除了超时之外什么都不做的地方。 内容永远不会到达服务器,并且永远不会执行.Continue代码。 但是,如果我连续运行它,或者甚至可以使用2或3个线程运行它可以正常工作。 似乎被抛出的线程越多,但性能会呈指数级下降

究竟我在这里做错了什么?

我不认为这是你问题的所在,但它可能是,并且它很容易实现和测试。 默认情况下,Windows将“最大网络”连接设置为2,并且可以在连接池上锁定超过2个线程。 您可以将其添加到您的应用配置中

      

或者在代码中你可以这样做

 ServicePointManager.DefaultConnectionLimit = 300 

我还考虑在继续中评论等待。 我不认为这是必要的。

 try { //Comment this line out your handling it in the outside task already //postTask.Wait(client.Timeout); //Don't wait longer than the client timeout. success = postTask.Result.IsSuccessStatusCode; }catch {} 

最后,如果上面的两件事不起作用,我会尝试评论这段代码。

 //Task.ConfigureAwait(false); 

可能是Task.Wait加上设置Task.ConfigureAwait(false)的组合导致某种死锁,但我不知道为什么。 我只知道我有一些非常类似的代码,运行multithreading就好了,我的代码中没有Task.ConfigureAwait(false),主要是因为我尝试了HttpClient库但没有升级到.NET 4.5所以等待不可用。

以下是使用当前代码向我发布的一些内容:

  • ContinueWith将委托排队,以在任务完成时运行。 所以没有必要等待它。
  • 这里不需要LongRunning ; 它会降低性能,因为你的延续非常快,根本不会长时间运行。
  • ConfigureAwait没有意义,因为没有await (并且无论如何都会丢弃返回值)。
  • 超时不需要传递给Task.Wait因为任务之后任务已经完成。

我有一个在便携式类库中制作的API,需要使用特定于平台的API来发送HTTP请求。

我建议你的API是异步的,因为它正在做HTTP。 如果要在PCL中获得完全async / await支持,可以使用Microsoft.Bcl.Async

 public async Task Post(IEnumerable> headers, string data) { HttpClient client = new HttpClient(new HttpClientHandler {AllowAutoRedirect = false}); foreach (var header in headers) { client.DefaultRequestHeaders.Add(header.Key, header.Value); } try { var result = await client.PostAsync(endpoint, new StringContent(data, Encoding.UTF8, "text/xml")).ConfigureAwait(false); return result.IsSuccessStatusCode; } catch { return false; } }