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

时间:2013-04-17 19:58:08

标签: c# networking windows-runtime dotnet-httpclient winrt-async

我在可移植类库中创建了一个API,它需要访问特定于平台的API以发送HTTP请求。这是我写的在WinRT上执行HTTP POST的方法:

   public bool Post(IEnumerable<KeyValuePair<string, string>> 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请求一样,这个方法将会到达 nothing 但超时的位置。内容永远不会到达服务器,并且永远不会执行.Continue代码。但是,如果我连续运行它,或者甚至可以使用2或3个线程运行它可以正常工作。看来,抛出更多线程会使性能呈指数级下降

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

3 个答案:

答案 0 :(得分:2)

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

<system.net>
  <connectionManagement>
    <add address="*" maxconnection="300" />
   </connectionManagement>
</system.net>

或在代码中你可以这样做

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)的组合导致了某种死锁,但我不知道为什么。我只知道我有一些非常类似的代码,运行多线程,我的代码中没有Task.ConfigureAwait(false),主要是因为我试用了HttpClient库但没有升级到.NET 4.5所以等待不可用。

答案 1 :(得分:0)

以下代码中包含了一些内容:

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

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

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

public async Task<bool> Post(IEnumerable<KeyValuePair<string, string>> 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;
    }
}

答案 2 :(得分:0)

当同时发出多个请求时,我也观察到了HttpClientHandler问题。 (.NET Framework 4.7.2)

我可以通过将.NET Core 2.1 SocketsHttpHandler反向移植到.NET Framework来解决此问题,并且当同时发出数十个多个请求时,backported implementation的性能得到了显着改善。