【发布时间】:2021-07-05 12:31:47
【问题描述】:
我们有一个混合使用异步和同步代码的 .NET 4.7.2(我理解这是一个禁忌)。我们在 Windows 服务上使用 NancyFX。该服务获取休息调用并进行休息调用。线程池看起来很健康(整个进程只使用 70 个线程)。出于某种原因,某些 http 响应会延迟 10 秒,有时会延迟 100 秒,从而导致任务取消。
这是代码的结构
public async Task<Guid> SomeFunction()
{
...
var response = await _httpClient.SendAsync(request, cancellationToken);
...
}
SomeFunction().Result
首先,我确信由于某种原因,响应在网络上的某个地方被延迟了。但是我们已经排除了多种方法,最重要的是通过 perfview 查看 ETW 跟踪并看到数据包几乎立即返回(使用 Microsoft-Windows-NDIS-PacketCapture/PacketFragment)
其次,我确信这与异步方法上的 .Result 代码引起的线程池问题有关。但是,进程上的线程再次保持稳定在 70 个线程。通过 perfview 我可以看到饥饿并没有发生(使用 Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThreadAdjustment/Adjustment)
我也想过我可能会遇到等待/异步和 .Result 的死锁情况,但死锁意味着请求永远不会完成,而不是它会延迟 10 秒。
我还仔细检查了我们只使用了一个 httpclient 实例,而且确实如此。
还能是什么?
此时我们正在删除 .Result 并将其替换为适当的 async/await。但我没有证据表明这会解决问题,因为我没有看到任何死锁或线程耗尽的证据。
我们也在考虑我们正在以某种方式耗尽 http 连接的建议。我认为不是这种情况的一个原因是,根据 perfview,请求被发送出去并且数据包返回,但响应并没有构成 c# 堆栈。但是,这些性能计数器可能表明正在排队。
更新 我们已经用这个增加了http连接,好像已经生效了。
<connectionManagement>
<add address="*" maxconnection="1024"/>
</connectionManagement>
上面显示的排队完全没有了。但是这些请求没有完成的问题仍然存在
【问题讨论】:
-
您正在创建多少个 HttpClient 实例?也许您的问题与打开 API 的最大连接数有关。看这里:aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
-
只使用一个实例——并且没有得到任何套接字异常来表明我们正在耗尽套接字
-
您能否尝试在应用程序的开头添加此语句:
ThreadPool.SetMinThreads(200, 200);,看看是否有什么不同?不建议将此作为修复方法,而是作为解决问题的一种方法。 -
@TheodorZoulias 我刚试过,这似乎可能减少了问题,但没有消除它。我需要等到明天早上才能将苹果与苹果进行比较,但问题仍然存在。如果这能解决问题,我会感到惊讶,因为在此更改之前,根据任务管理器的线程数徘徊在 71(有足够的增长空间)。现在线程悬停在 140,这也很奇怪,因为我要求它从 200 开始。
-
仅供参考
SetMinThreadsAPI 没有立即创建指定数量的线程的效果。它只是确保通过创建一个新线程立即满足所有对后台工作的请求,直到达到此阈值。之后,ThreadPool切换到保守算法,等待 500 毫秒让当前正在运行的作业完成,然后将新线程注入池中。
标签: c# async-await threadpool dotnet-httpclient