HttpClient.PostAsync ThreadPool 中没有足够的空闲线程来完成操作

HttpClient.PostAsync There were not enough free threads in the ThreadPool to complete the operation

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

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] ERROR CameraHttpClient AggregateException System.AggregateException: One or more errors occurred. ---> System.InvalidOperationException: There were not enough free threads in the ThreadPool to complete the operation.

调用 GetEventsSince 的父线程是循环中的 background worker 线程 运行 如果有任何区别的话。

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

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

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

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

这对根应用程序调用此方法的方式有影响。我需要查看该代码以建议最佳方法。 TPL Dataflow 可能很适合这里 - 它不仅有助于定期调用异步方法,而且还支持限制并发性作为防止此类问题的一种保护措施。