Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用字典线程是否安全?_C#_.net_Thread Safety - Fatal编程技术网

C# 使用字典线程是否安全?

C# 使用字典线程是否安全?,c#,.net,thread-safety,C#,.net,Thread Safety,这里是我的方法的两个变体,它返回一个与枚举值(保存在字典中)关联的字符串。第一个变体速度较慢,但线程安全,第二个更快,但我不知道它是否线程安全。第一: string GetStringForEnum (SomeEnum e) { string str = null; lock (someDictionary) //someDictionary is not used anywhere else (only in this method) { if (!someDictionar

这里是我的方法的两个变体,它返回一个与枚举值(保存在字典中)关联的字符串。第一个变体速度较慢,但线程安全,第二个更快,但我不知道它是否线程安全。第一:

string GetStringForEnum (SomeEnum e)
{
   string str = null;
   lock (someDictionary) //someDictionary is not used anywhere else (only in this method)
   { if (!someDictionary (e, out str)) { someDictionary.Add (e, "somehowCreatedString"); }
   return str;
}
第二种变体:

string GetStringForEnum (SomeEnum e)
{
   string str = null;
   if (!someDictionary (e, out str))
   {
     lock (someDictionary) //someDictionary is not used anywhere else (only in this method)
     { if (!someDictionary (e, out str)) { someDictionary.Add (e, "somehowCreatedString"); }
   }
   return str;
}

第二个变体不是每次都使用“锁定”,但它是否是线程安全的?

如果使用.NET 4,则可以使用
ConcurrentDictionary
实现线程安全。

on
Dictionary
中有一节介绍线程安全:

字典(TKey,TValue)
可以支持多个阅读器 同时,只要集合未被修改。即便如此, 通过集合枚举本质上不是线程安全的 程序在枚举与写入争用的罕见情况下 访问时,必须在整个枚举期间锁定集合。 允许多个线程访问集合以进行读取 在编写时,您必须实现自己的同步

有关线程安全的替代方案,请参见TKey的ConcurrentDictionary, t值)

因此:

  • 可以从多个线程安全使用,因为它是只读的。但是,当其他代码同时编写字典时(您的代码正在编写字典),这是不安全的
  • 除非锁定字典,否则添加值永远不会安全
  • 使用
    ConcurrentDictionary
    是一个简单的解决方案,但可能不会比第一个版本快(我假设它会锁定每个操作)

还有一个旁注:不建议使用非私有的字段(这里的字段是
someDictionary
,我们不知道它是否是
private
)作为
lock
目标,因为理论上外部代码也可以在您不知情的情况下决定
lock
它(实际上这不会发生,但为什么理论上不正确呢?)

这里有两个问题:

  • lock(someDictionary)
    -即使字典没有在其他地方使用,也不建议这样做。这是一个理论上的论点,但dictionary类的(未来)代码可能会锁定自身

  • 如果(!someDictionary(e,out str))
    没有锁。我假设这是对
    TryGetValue()
    的调用。这根本不是线程安全的,您的读取可能会被另一个线程中的写入中断。这可能会导致各种错误(索引超出范围,空引用)。错误将非常罕见(=难以重现)


这是假设的真实代码吗?我不能对
!someDictionary(e,out str)
进行小的更正。您说“TryGetValue可以从多个线程安全使用,因为它是只读的。”,但这只适用于“只要不修改集合”。OP给出的例子清楚地表明,他实际上是在更新字典,这使得在锁外使用
TryGetValue
不安全。不安全。对于你的其余答案:+1。@Steven:我可能应该说得很清楚。因此编辑了答案,感谢反馈。“但是(未来)Dictionary类的代码可能会锁定自身。“。如果字典这样做,它将打破一个强有力的指导方针,这也将是一个突破性的变化。因此,可以安全地假设字典永远不会锁定自己。@Steven-你是对的,就像人们所说的“理论”。最佳做法是使用一个你尽可能控制和隐藏的实例。@HenkHolterman:我必须这样做假设我在编写LOB应用程序时非常务实,只需锁定私有集合(如果可能)。另一方面,在为可重用库编写代码时,我非常严格。这都是关于上下文;-)是的,我强烈建议使用System.Collections.Concurrent命名空间中的集合,而不是使用锁。如果不可能,则使用ReaderWriterLockSlim。