gpt4 book ai didi

c# - .NET:由于字典,HttpClient 中的 CPU 使用率为 100%?

转载 作者:太空狗 更新时间:2023-10-29 19:46:40 24 4
gpt4 key购买 nike

小问题:
有没有其他人在使用单例 .NET HttpClient 时遇到问题,其中应用程序将处理器固定在 100%,直到它重新启动?

详情:
我正在运行一个 Windows 服务,该服务执行连续的、基于计划的 ETL。数据同步线程之一偶尔会死掉,或者开始失控并使处理器保持在 100%。

我很幸运在有人简单地重新启动服务(标准修复)并能够获取转储文件之前看到了这种情况。

在 WinDbg(带 SOS 和 SOSEX)中加载它,我发现我有大约 15 个线程(主处理线程的子任务)都以相同的堆栈跟踪运行。但是,似乎没有任何僵局。 IE。高利用率线程正在运行,但从未完成。

相关的堆栈跟踪段如下(地址省略):

System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].FindEntry(System.__Canon)
System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].TryGetValue(System.__Canon, System.__Canon ByRef)
System.Net.Http.Headers.HttpHeaders.ContainsParsedValue(System.String, System.Object)
System.Net.Http.Headers.HttpGeneralHeaders.get_TransferEncodingChunked()
System.Net.Http.Headers.HttpGeneralHeaders.AddSpecialsFrom(System.Net.Http.Headers.HttpGeneralHeaders)
System.Net.Http.Headers.HttpRequestHeaders.AddHeaders(System.Net.Http.Headers.HttpHeaders)
System.Net.Http.HttpClient.SendAsync(System.Net.Http.HttpRequestMessage, System.Net.Http.HttpCompletionOption, System.Threading.CancellationToken)
...
[Our Application Code]

根据 this article (和其他我发现的),字典的使用不是线程安全的,如果您以多线程方式访问字典,则可能会出现无限循环(就像直接崩溃一样) .

但是 我们的应用程序代码没有明确使用字典。那么堆栈跟踪中提到的字典在哪里?

通过 .NET Reflector,似乎 HttpClient 使用字典来存储已在“DefaultRequestHeaders”属性中配置的任何值。因此,通过 HttpClient 发送的任何请求都会触发单例非线程安全字典的枚举(以便将默认 header 添加到请求中),这可能会无限地旋转(或杀死)所涉及的线程如果发生腐败。

微软直言HttpClient类是线程安全的。但在我看来,如果任何 header 已添加到 HttpClient 的 DefaultRequestHeaders 中,这就不再成立了。

我的分析似乎表明这是真正的根本问题,一个简单的解决方法是永远不要在可以以多线程方式使用 HttpClient 的地方使用 DefaultRequestHeaders。

但是,我正在寻找一些证据来证明我没有找错树。如果这是正确的,这似乎是 .NET 框架中的错误,我自然而然地倾向于怀疑。

抱歉这个冗长的问题,但感谢您提供的任何意见。

最佳答案

感谢所有评论;他们让我从不同的角度思考问题,并帮助我找到了问题的最终根源。

尽管问题 DefaultRequestHeaders 支持字典损坏的结果,但真正的罪魁祸首是 HttpClient 对象的初始化代码:

private HttpClient InitializeClient()
{
if (_client == null)
{
_client = GetHttpClient();
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
SetBaseAddress(BaseAddress);
}
return _client;
}

我说 HttpClient 是一个单例,这是部分不正确的。它是作为单个实例创建的,在执行一个工作单元的多个线程之间共享,并在工作完成时被释放。下次必须完成此特定任务时,将启动一个新实例。

上面的“InitializeClient”方法在每次发送请求时都会被调用,并且应该只是因为“_client”字段在第一次运行后不为空而短路。

(请注意,这不是在对象的构造函数中完成的,因为它是一个抽象类,而“GetHttpClient”是一个抽象方法——顺便说一句:永远不要在基类的构造函数中调用抽象方法。 .这会导致其他噩梦)

当然,很明显这不是线程安全的,并且由此产生的行为是不确定的。

解决方法是将此代码放在经过双重检查的“锁定”语句后面(尽管我无论如何都会消除对“DefaultRequestHeaders”属性的使用,只是因为)。

简而言之,如果您在初始化 HttpClient 时小心谨慎,我最初的问题就不会成为问题。

感谢你们提供的思路清晰!

关于c# - .NET:由于字典,HttpClient 中的 CPU 使用率为 100%?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36774293/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com