什么时候应该使用 ConcurrentDictionary 和 Dictionary?
When should I use ConcurrentDictionary and Dictionary?
我总是对选择其中的哪一个感到困惑。正如我所见,如果我想要两种数据类型作为 Key
和 Value
,我会在 List
上使用 Dictionary
,这样我可以很容易地通过它的 key
找到一个值但是我总是很困惑我应该使用 ConcurrentDictionary
还是 Dictionary
?
在你因为我没有对此进行太多研究而对我大发雷霆之前,我已经尝试过了,但似乎 google 在 Dictionary
和 ConcurrentDictionary
上并没有真正得到任何东西,但是每个人身上都有一些东西。
我以前问过一个朋友,但他们只说:"use ConcurrentDictionary
if you use your dictionary a lot in code"我真的不想纠缠他们更详细地解释它。任何人都可以对此进行扩展吗?
当您需要跨多个线程(即多线程)访问字典时,ConcurrentDictionary
很有用。原版 Dictionary
对象不具备此功能,因此只能以 single-threaded 方式使用。
使用 ConcurrentDictionary
而不是常规 Dictionary
的最大原因是线程安全。如果您的应用程序将获得多个线程同时使用同一个字典,您需要 thread-safe ConcurrentDictionary
,当这些线程正在写入或构建字典时尤其如此。
在没有 multi-threading 的情况下使用 ConcurrentDictionary
的缺点是开销。所有那些允许它 thread-safe 的函数仍然存在,所有的锁和检查仍然会发生,占用处理时间并使用额外的内存。
“如果您在代码中经常使用词典,请使用 ConcurrentDictionary”是一种模糊的建议。我不怪你造成混乱。
ConcurrentDictionary
主要用于从多线程(或异步任务)更新字典的环境。如果它来自单个线程,您可以从任意多的代码中使用标准 Dictionary
;)
如果您查看 ConcurrentDictionary 上的方法,您会发现一些有趣的方法,例如 TryAdd
、TryGetValue
、TryUpdate
和 TryRemove
。
例如,考虑一个典型的模式,您可能会看到使用普通 Dictionary
class.
// There are better ways to do this... but we need an example ;)
if (!dictionary.ContainsKey(id))
dictionary.Add(id, value);
这有一个问题,在检查它是否包含密钥和调用 Add
之间,不同的线程可以使用相同的 id
调用 Add
。当这个线程调用Add
时,它会抛出一个异常。方法 TryAdd
会为您处理,并将 return 一个 true/false 告诉您它是否添加了它(或者该键是否已经在字典中)。
因此,除非您在 multi-threaded 代码部分工作,否则您可能只使用标准 Dictionary
class。话虽如此,理论上您可以使用锁来防止并发访问字典; "Dictionary locking vs. ConcurrentDictionary".
中已经解决了这个问题
A ConcurrentDictionary
当你想要一个可以被多个线程同时安全访问的 high-performance 字典时很有用。与受 lock
保护的标准 Dictionary
相比,由于其细粒度锁定实现,它在大量使用时效率更高。 ConcurrentDictionary
不是所有线程都竞争一个锁,而是在内部维护多个锁,以这种方式最大限度地减少争用,并限制成为瓶颈的可能性。
尽管有这些不错的特性,但使用 ConcurrentDictionary
是最佳选择的场景实际上很少。有两个原因:
ConcurrentDictionary
提供的 thread-safety 保证仅限于对其内部状态的保护。而已。如果你想做一些稍微 non-trivial 的事情,比如更新字典 和 另一个变量作为原子操作,你就不走运了。 ConcurrentDictionary
不支持这种情况。甚至不支持保护它包含的元素(如果它们是可变对象)。如果您尝试使用 AddOrUpdate
方法更新其中一个值,字典将受到保护,但值不会。 Update
在此上下文中的意思是用另一个值替换现有值,而不是修改现有值。
每当您觉得使用 ConcurrentDictionary
时,通常都有更好的选择。不涉及共享状态的替代方案,这就是 ConcurrentDictionary
的本质。无论它的锁定方案多么有效,它都很难击败根本没有共享状态的架构,每个线程都在不干扰其他线程的情况下做自己的事情。遵循这一原则的常用库是 PLINQ and the TPL Dataflow 库。下面是一个 PLINQ 示例:
Dictionary<string, Product> dictionary = productIDs
.AsParallel()
.Select(id => GetProduct(id))
.ToDictionary(product => product.Barcode);
您可以相信 PLINQ 可以使用更有效的策略生成字典,包括对初始工作负载进行分区,并将每个分区分配给不同的任务,而不是预先创建一个字典,然后让多个线程同时用值填充它工作线程。单个线程最终会聚合部分结果,并填充字典。
上面接受的答案是正确的。然而,值得一提的是,如果字典没有被修改,即它只被读取,而不管线程的数量,那么 Dictionary<TKey,TValue>
是首选,因为不需要同步。
例如在 Dictionary<TKey,TValue>
中缓存配置,仅在启动时填充一次,并在整个应用程序的整个生命周期中使用。
When to use a thread-safe collection : ConcurrentDictionary vs. Dictionary
If you are only reading key or values, the Dictionary<TKey,TValue> is faster because no synchronization is required if the dictionary is not being modified by any threads.
我总是对选择其中的哪一个感到困惑。正如我所见,如果我想要两种数据类型作为 Key
和 Value
,我会在 List
上使用 Dictionary
,这样我可以很容易地通过它的 key
找到一个值但是我总是很困惑我应该使用 ConcurrentDictionary
还是 Dictionary
?
在你因为我没有对此进行太多研究而对我大发雷霆之前,我已经尝试过了,但似乎 google 在 Dictionary
和 ConcurrentDictionary
上并没有真正得到任何东西,但是每个人身上都有一些东西。
我以前问过一个朋友,但他们只说:"use ConcurrentDictionary
if you use your dictionary a lot in code"我真的不想纠缠他们更详细地解释它。任何人都可以对此进行扩展吗?
ConcurrentDictionary
很有用。原版 Dictionary
对象不具备此功能,因此只能以 single-threaded 方式使用。
使用 ConcurrentDictionary
而不是常规 Dictionary
的最大原因是线程安全。如果您的应用程序将获得多个线程同时使用同一个字典,您需要 thread-safe ConcurrentDictionary
,当这些线程正在写入或构建字典时尤其如此。
在没有 multi-threading 的情况下使用 ConcurrentDictionary
的缺点是开销。所有那些允许它 thread-safe 的函数仍然存在,所有的锁和检查仍然会发生,占用处理时间并使用额外的内存。
“如果您在代码中经常使用词典,请使用 ConcurrentDictionary”是一种模糊的建议。我不怪你造成混乱。
ConcurrentDictionary
主要用于从多线程(或异步任务)更新字典的环境。如果它来自单个线程,您可以从任意多的代码中使用标准 Dictionary
;)
如果您查看 ConcurrentDictionary 上的方法,您会发现一些有趣的方法,例如 TryAdd
、TryGetValue
、TryUpdate
和 TryRemove
。
例如,考虑一个典型的模式,您可能会看到使用普通 Dictionary
class.
// There are better ways to do this... but we need an example ;)
if (!dictionary.ContainsKey(id))
dictionary.Add(id, value);
这有一个问题,在检查它是否包含密钥和调用 Add
之间,不同的线程可以使用相同的 id
调用 Add
。当这个线程调用Add
时,它会抛出一个异常。方法 TryAdd
会为您处理,并将 return 一个 true/false 告诉您它是否添加了它(或者该键是否已经在字典中)。
因此,除非您在 multi-threaded 代码部分工作,否则您可能只使用标准 Dictionary
class。话虽如此,理论上您可以使用锁来防止并发访问字典; "Dictionary locking vs. ConcurrentDictionary".
A ConcurrentDictionary
当你想要一个可以被多个线程同时安全访问的 high-performance 字典时很有用。与受 lock
保护的标准 Dictionary
相比,由于其细粒度锁定实现,它在大量使用时效率更高。 ConcurrentDictionary
不是所有线程都竞争一个锁,而是在内部维护多个锁,以这种方式最大限度地减少争用,并限制成为瓶颈的可能性。
尽管有这些不错的特性,但使用 ConcurrentDictionary
是最佳选择的场景实际上很少。有两个原因:
ConcurrentDictionary
提供的 thread-safety 保证仅限于对其内部状态的保护。而已。如果你想做一些稍微 non-trivial 的事情,比如更新字典 和 另一个变量作为原子操作,你就不走运了。ConcurrentDictionary
不支持这种情况。甚至不支持保护它包含的元素(如果它们是可变对象)。如果您尝试使用AddOrUpdate
方法更新其中一个值,字典将受到保护,但值不会。Update
在此上下文中的意思是用另一个值替换现有值,而不是修改现有值。每当您觉得使用
ConcurrentDictionary
时,通常都有更好的选择。不涉及共享状态的替代方案,这就是ConcurrentDictionary
的本质。无论它的锁定方案多么有效,它都很难击败根本没有共享状态的架构,每个线程都在不干扰其他线程的情况下做自己的事情。遵循这一原则的常用库是 PLINQ and the TPL Dataflow 库。下面是一个 PLINQ 示例:
Dictionary<string, Product> dictionary = productIDs
.AsParallel()
.Select(id => GetProduct(id))
.ToDictionary(product => product.Barcode);
您可以相信 PLINQ 可以使用更有效的策略生成字典,包括对初始工作负载进行分区,并将每个分区分配给不同的任务,而不是预先创建一个字典,然后让多个线程同时用值填充它工作线程。单个线程最终会聚合部分结果,并填充字典。
上面接受的答案是正确的。然而,值得一提的是,如果字典没有被修改,即它只被读取,而不管线程的数量,那么 Dictionary<TKey,TValue>
是首选,因为不需要同步。
例如在 Dictionary<TKey,TValue>
中缓存配置,仅在启动时填充一次,并在整个应用程序的整个生命周期中使用。
When to use a thread-safe collection : ConcurrentDictionary vs. Dictionary
If you are only reading key or values, the Dictionary<TKey,TValue> is faster because no synchronization is required if the dictionary is not being modified by any threads.