简体   繁体   English

这是否使用字典线程安全?

[英]Is this using of dictionary thread-safe or not?

Here is two my variants of method, which returns a string, associated with enum value (kept in dictionary).这是我的两个方法变体,它返回一个字符串,与枚举值相关联(保存在字典中)。 First variant is slower, but thread-safe, second is faster, but i don't know, whether it is thread-safe or not.第一个变体较慢,但线程安全,第二个变体更快,但我不知道它是否是线程安全的。 First:第一的:

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

Second variant:第二种变体:

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

Second variant is not used "lock" each time, but is it thread-safe or not?第二种变体不是每次都使用“锁”,但它是否是线程安全的?

There are 2 problems here:这里有两个问题:

  • lock (someDictionary) - this is dis-advised, even if the dictionary is not used elsewhere. lock (someDictionary) - 这是不建议的,即使字典没有在其他地方使用。 It is a theoretical argument but the (future) code of the Dictionary class could lock on itself.这是一个理论上的争论,但字典 class 的(未来)代码可能会锁定自己。

  • if (,someDictionary (e, out str)) without a lock. if (,someDictionary (e, out str))没有锁。 I assume this is a call to TryGetValue() .我假设这是对TryGetValue()的调用。 This simply is not thread-safe, your Read could be interrupted by a Write in another thread.这根本不是线程安全的,您的读取可能会被另一个线程中的写入中断。 This could end in all sorts of errors (index out of range, null reference).这可能会以各种错误结束(索引超出范围,null 参考)。 The errors will be very rare (= hard to reproduce).错误将非常罕见(= 难以重现)。

The documentation on Dictionary has a section on thread safety: Dictionary上的文档有一个关于线程安全的部分:

A Dictionary(Of TKey, TValue) can support multiple readers concurrently, as long as the collection is not modified .只要集合未被修改Dictionary(Of TKey, TValue)就可以同时支持多个读者。 Even so, enumerating through a collection is intrinsically not a thread-safe procedure.即便如此,通过集合进行枚举本质上不是线程安全的过程。 In the rare case where an enumeration contends with write accesses, the collection must be locked during the entire enumeration.在枚举与写访问争用的极少数情况下,必须在整个枚举期间锁定集合。 To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.要允许多个线程访问集合以进行读写,您必须实现自己的同步。

For a thread-safe alternative, see ConcurrentDictionary(Of TKey, TValue) .有关线程安全的替代方法,请参阅ConcurrentDictionary(Of TKey, TValue)

So:所以:

  • TryGetValue is safe to use from multiple threads because it is read-only. TryGetValue是只读的,因此可以安全地从多个线程使用。 However, it is not safe when other code is writing the dictionary at the same time (which your code is doing).但是,当其他代码同时写入字典(您的代码正在这样做)时,这是不安全的。
  • Adding a value is never safe unless you lock the dictionary.添加一个值永远不会安全,除非你锁定字典。
  • Using a ConcurrentDictionary is an easy solution to get going, but it may be no faster than your first version (I assume it's going to lock on every operation).使用ConcurrentDictionary是一个简单的解决方案,但它可能不会比您的第一个版本快(我假设它会锁定每个操作)。

And a side note: Using a field that is not private (here the field is someDictionary and we don't know if it's private or not) as a lock target is not recommended because theoretically external code could also decide to lock it without your knowledge (practically this is not going to happen, but why not be theoretically correct as well?).附注:不建议使用非私有字段(这里的字段是someDictionary ,我们不知道它是否是private的)作为lock目标,因为理论上外部代码也可以在您不知情的情况下决定lock它(实际上这不会发生,但为什么在理论上也不正确呢?)。

If you use .NET 4 you can use ConcurrentDictionary for thread safety.如果使用 .NET 4,则可以使用ConcurrentDictionary来确保线程安全。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM