c# - HttpClient 查询偶尔挂起

标签 c# async-await timeout httpclient

我初始化 HttpClient像这样:

public static CookieContainer cookieContainer = new CookieContainer();
public static HttpClient httpClient = new HttpClient(new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, CookieContainer = cookieContainer }) { Timeout = TimeSpan.FromSeconds(120) };

所以所有查询都应该抛出 TaskCanceledException如果在 120 秒内没有收到响应。
但是某些查询(例如 1 of 100 000-1 000 000)会无限挂起。

我写了以下代码:
public static async Task<HttpResponse> DownloadAsync2(HttpRequestMessage httpRequestMessage)
{
    HttpResponse response = new HttpResponse { Success = false, StatusCode = (int)HttpStatusCode.RequestTimeout, Response = "Timeout????????" };
    Task task;
    if (await Task.WhenAny(
        task = Task.Run(async () =>
        {
            try
            {
                HttpResponseMessage r = await Global.httpClient.SendAsync(httpRequestMessage).ConfigureAwait(false);
                response = new HttpResponse { Success = true, StatusCode = (int)r.StatusCode, Response = await r.Content.ReadAsStringAsync().ConfigureAwait(false) };
            }
            catch (TaskCanceledException)
            {
                response = new HttpResponse { Success = false, StatusCode = (int)HttpStatusCode.RequestTimeout, Response = "Timeout" };
            }
            catch (Exception ex)
            {
                response = new HttpResponse { Success = false, StatusCode = -1, Response = ex.Message + ": " + ex.InnerException };
            }
        }),
        Task.Run(async () =>
        {
            await Task.Delay(TimeSpan.FromSeconds(150)).ConfigureAwait(false);
        })
    ).ConfigureAwait(false) != task)
    {
        Log("150 seconds passed");
    }
    return response;
}

实际上偶尔会执行 Log("150 seconds passed"); .

我这样称呼它:
HttpResponse r = await DownloadAsync2(new HttpRequestMessage
{
    RequestUri = new Uri("https://address.com"),
    Method = HttpMethod.Get
}).ConfigureAwait(false);

为什么TaskCanceledException有时120秒后不会抛出?

最佳答案

我不知道你打电话的频率是多少DownloadAsync2 ,但你的代码闻起来很香线程池爆裂和饥饿 .
默认情况下,ThreadPool 中的初始线程数仅限于 CPU 逻辑内核的数量(对于今天的普通系统通常为 12),并且如果 ThreadPool 中的线程不可用,则每个新线程的生成需要 500 毫秒。
例如:

for (int i = 0; i < 1000; i++)
{
    HttpResponse r = await DownloadAsync2(new HttpRequestMessage
    {
        RequestUri = new Uri("https://address.com"),
        Method = HttpMethod.Get
    }).ConfigureAwait(false);
}
这个代码很有可能会被卡住,特别是如果你有一些 lock或任何 cpu intensive代码中某处的任务。因为您每次调用都会调用新线程 DownloadAsync2所以 ThreadPool 的所有线程都被消耗掉了,而且还需要更多的线程。
我知道您可能会说“我的所有任务都已等待并发布用于其他作品”。但他们也消耗了开始新的 DownloadAsync2线程,您将到达完成后的点 await Global.httpClient.SendAsync没有线程可以重新分配和完成任务。
所以方法必须等到一个线程可用或生成完成(即使在超时之后)。罕见但可行。

关于c# - HttpClient 查询偶尔挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53925346/

相关文章:

c# - 如何在 WebClient.DownloadFileAsync 上实现超时

c# - 如何将 100 行插入到只有标识列的单个表中?

c# - 模拟礼品卡金额减少的 DDD 服务或实体

c# - 使用一次性对象的回调取消操作的正确方法是什么?

c# - 将 WPF 视觉对象异步渲染为位图

c# - WebClient.DownloadFileTaskAsync() 实际上永远不会超时吗?

C# - GetMethod 返回 null

c# - 使用 Rfc2898DeriveBytes 将 C# PBKDF2 转换为 PHP

java - 当 JSF/Spring 中的 session 超时时重定向到登录或当 session 停用(或给定时间空闲)时自动注销

javascript - 刷新/定时器功能仅适用于 2 个提要中的 1 个 -- $q.all() 合并