【问题标题】:HttpClient.PostAsync There were not enough free threads in the ThreadPool to complete the operationHttpClient.PostAsync 线程池中没有足够的空闲线程来完成操作
【发布时间】:2015-08-17 09:14:45
【问题描述】:

我每秒调用一次以下代码来轮询相机,但运行一两天后,它停止工作。

public List<CameraEvent> GetEventsSince(CaptureTime afterDateTime)
{
    string uri = string.Format(
                    "http://{0}/ISAPI/channels/1/events/detect", _ipAddress);
    using (var client = new HttpClient())
    {
        client.Timeout = TimeSpan.FromSeconds(5);
        AddBasicAuth(client);

        try
        {
            HttpResponseMessage response =
                client.PostAsync(
                    uri, new StringContent(GetPicTimeXml(afterDateTime))).Result;

            logger.Debug(
                string.Format("Status code response={0}", response.StatusCode));

            if (response.StatusCode == HttpStatusCode.Unauthorized ||
                response.StatusCode == HttpStatusCode.Forbidden)
            {
                // 401
                currentState = 2;
                return new List<CameraEvent>();
            }

            if (response.StatusCode == HttpStatusCode.OK)
            {
                // OK
                currentState = 0;
            }
            List<CameraEvent> events = new CameraHttpResponseHandler()
                                           .HandleHttpResponse(response);
            AppendPlateImages(events);
            return events;
        }
        catch (AggregateException ex)
        {
            //if (ex.InnerException is TaskCanceledException)
            //{
            //    // Timeout
            //    currentState = 1;
            //}
            logger.Error("AggregateException", ex);
        }
        catch (Exception ex)
        {
            logger.Error("Generic exception", ex);
        }

        return new List<CameraEvent>();
    }
}

我得到的错误是:

2015-08-17 07:59:57,310 [16] 错误 CameraHttpClient AggregateException System.AggregateException:发生一个或多个错误。 ---> System.InvalidOperationException:没有足够的空闲线程 在 ThreadPool 中完成操作。

调用GetEventsSince 的父线程是一个在循环中运行的background worker 线程,如果这有什么不同的话。

有没有人看到这个问题或对可能导致线程用完的原因有任何建议?

【问题讨论】:

  • 也许问题是您在using 块内返回?也许这会阻止 HttpClient 被处置。
  • 我做了一些阅读,如果我在 using 块内返回也没关系,因为任何出口都应该调用 dispose。
  • 从开始到结束,此方法运行通常需要多长时间?你在做任何日志记录吗?如果有什么东西挂了,而你每秒都在抓一个新线程,那只是时间问题。
  • 这段代码不异步有什么原因吗?
  • @DavidHawkins 我在 HttpClient.PostAsync 上遇到了类似的问题,并且我正在查看的代码也会立即访问 .Result 属性,正如 Todd Menier 在下面解释的那样,这会导致阻塞。但是我的问题是你是如何解决这个问题的?

标签: c# multithreading http threadpool


【解决方案1】:

很难确定,但如果线程池饥饿的根本原因是这种方法,那么这是一个很好的例子,说明为什么异步代码对服务器有益。

HttpClient 是一个异步 API,这意味着如果您正确地 await 调用,您将释放一个线程并将其发送回线程池,直到调用返回。通过调用.Result,您将在整个调用期间阻塞线程。假设这个方法从开始到结束需要几秒钟,并且 99.9% 的时间都在等待 I/O(这不是一个不合理的猜测)。事实上,你 100% 的时间都在消耗一个线程。如果你重构它以异步运行,你的线程消耗下降到 0.1% 的时间,线程池平均突然变得更满了。

所以我将首先标记方法async(使用Task&lt;List&lt;CameraEvent&gt;&gt; 作为返回类型)并使用await 而不是使用异步API 的.Result。我不知道 CameraHttpResponseHandler.HandleHttpResponse 到底做了什么,但我猜那里的 I/O 也有阻塞,它也应该被转换并使用 await 调用。

这会影响根应用程序如何调用此方法。我需要查看该代码以提出最佳方法。 TPL Dataflow 可能很适合这里 - 它不仅有助于定期调用异步方法,而且还支持限制并发,以防止此类问题。

【讨论】:

    猜你喜欢
    • 2011-08-19
    • 2021-06-03
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    相关资源
    最近更新 更多