【问题标题】:Reproducing the 'non-concurrent collections must have exclusive access' exception重现“非并发集合必须具有独占访问权限”异常
【发布时间】:2021-11-01 08:38:23
【问题描述】:

我在生产应用程序中的https://dotnetfiddle.net/GknA5Q 中描述了以下代码。 dictionary 用作缓存 来保存对象的属性。使用此dictionaryREST API 有数百种对象类型,因此当API 找到更多对象类型时,缓存会变大。正如您在代码中看到的那样,对象的属性是通过 reflection 检索的。当 API 启动并且数百个请求到达 API 时,dictionary 会被填充。每个请求都有一个对象类型,其属性正在被缓存。

虽然 REST API 有效,但现在当 API 在 IIS 中启动时,代码会生成以下错误。产生错误的行号是 23,其中调用了 TryGetValue

更改非并发集合的操作必须具有独占访问权限。对此集合执行了并发更新并损坏了它的状态。集合的状态不再正确。

我正在尝试在测试应用程序中重现相同的错误,因此可以应用一些解决方案。我不想删除dictionary,这会增加处理到达 API 的每个请求中的每个模型的时间。如果这个dictionary缓存可用,则可以从缓存中检索属性,而不是通过反射进行解析。

如何重现上述错误?

【问题讨论】:

  • ConcurrentDictionary<Key, Value> 替换它?复制将是非常失败的。你不使用测试来确定代码是否没有竞争条件,你可以推理它。
  • 种族条件就其本质而言很难重现(我每 2 年看到一次像这种表面这样的错误)。但显然它正在发生。所以现在就换成 ConcurrentDictionary,就像@BenVoigt 说的那样(或使用锁定,取决于问题的性质)。

标签: c# dictionary collections


【解决方案1】:

Dictionary 类支持多个读取器,但不支持多个写入器。

你可以在这里阅读官方文档
https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=net-5.0#thread-safety

要测试并发性,您可以使用 Parallel
https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallel?view=net-5.0

Parallel.For(0, 1000, i =>
{
     // dictionary.Add(...)
});

IDictionary 的线程安全实现是 ConcurrentDictionary
https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=net-5.0

您只需将代码中的“Dictionary”替换为“ConcurrentDictionary”

【讨论】:

  • You just need to replace "Dictionary" in you code with "ConcurrentDictionary" 过于简单化(因为某些 LINQ 运算符不是线程安全的)。但是,是的,作为一个更好的切换起点。
  • 我将Dictionary 更改为ConcurrentDictionary 并与Parallel.For(...) 同时运行它。我收到错误字典中已存在密钥。如果我将dictionary.Add(...) 更改为dictionary.AddOrUpdate(...),错误会消失吗?
  • 当我使用ConcurrentDictionary时我应该使用dictionary.AddOrUpdate(...)吗?
  • 是的。使用 AddOrUpdate 而不是 Add
猜你喜欢
  • 2020-06-26
  • 1970-01-01
  • 2011-10-10
  • 2018-12-12
  • 2017-03-31
  • 2015-07-22
  • 2022-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多