3

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

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;
}

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

4

3 回答 3

4

这里有2个问题:

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

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

于 2012-04-04T08:13:30.840 回答
3

上的文档Dictionary有一个关于线程安全的部分:

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

有关线程安全的替代方案,请参阅ConcurrentDictionary(Of TKey, TValue)

所以:

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

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

于 2012-04-04T08:08:38.033 回答
2

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

于 2012-04-04T08:08:03.820 回答