1

我有一个在可移植类库中制作的 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 请求一样,此方法将到达除了超时之外什么都不做的地方。内容永远不会到达服务器,.Continue代码也永远不会执行。但是,如果我连续运行它,或者甚至使用 2 或 3 个线程,它就可以正常工作。似乎抛出的线程越多,性能越差

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

4

3 回答 3

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所以 await 不可用。

于 2013-04-17T20:19:15.133 回答
0

以下是当前代码让我印象深刻的一些事情:

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

我有一个在可移植类库中制作的 API,它需要访问特定于平台的 API 以发送 HTTP 请求。

我建议使您的 API 异步,因为它正在执行 HTTP。Microsoft.Bcl.Async如果您想在 PCL 中获得完整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;
    }
}
于 2013-04-18T00:18:55.600 回答
0

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

我能够通过将 .NET Core 2.1 SocketsHttpHandler 反向移植到 .NET Framework 来解决该问题,并且当同时发出数十个多个请求时,反向移植的实现显着提高了性能。

于 2020-04-04T09:26:14.900 回答