线程安全的字典 C#

3

我的同事最近说,如果您不介意获取过期的数据,多个读写线程访问c#字典是可以的。他的理由是,由于程序会重复从字典中读取,因此过期的数据不会成为问题。

我告诉他,当您有一个编写器线程时,锁定集合始终是必要的,因为集合的内部状态将会被破坏。

我错了吗?


他说,每当集合检测到无效的并发访问时,就会抛出异常。然后他可以捕获异常并重试。他说他想最小化锁定以提高性能。只捕捉异常然后重试是否可行? - Koda
3个回答

8

您是正确的,而您的同事是错误的:只有在没有写入器的情况下,才能从多个线程访问字典。

.NET 4.0 添加了 ConcurrentDictionary<K,T> 类,它恰好做了其名称所示的功能。


1
尽管使用 ConcurrentDictionary 可以加分,但仅依赖它并不足以真正理解其内部工作机制。Concurrent 集合提供了许多常见的多步骤进程的函数,将多个锁定操作转化为单一的管理进程,但它们并非万能替代品。 - Adam Robinson
@AdamRobinson 你对“一刀切”的评论非常到位:没有什么比了解正在发生的事情更重要的了。 - Sergey Kalinichenko
他说,每当集合检测到无效的并发访问时,就会抛出异常。然后他可以捕获异常并重试。他说他想尽量减少锁定以提高性能。只捕获异常并重试是否可行? - Koda
@Alvin 不行,这个方法也不可行:问题在于集合并不总是能检测到并发修改。除了偶尔会出现索引超出范围异常之外,TryGetValue[] 并不进行任何并发检查。它们有时会返回错误键的值而不是抛出异常,因此您将不知道要重试。 - Sergey Kalinichenko
@Alvin 他为什么要进行优化呢?是分析器告诉他ConcurrentDictionary内部的锁定是系统瓶颈,还是这是一种过早的优化? - Sergey Kalinichenko
显示剩余3条评论

1

你说得对,写入操作确实需要某种形式的锁定,但仅仅拥有写入权限并不意味着每次访问集合都必须使用lock() { }

正如你所说,内置集合的非同步版本仅在读取时是线程安全的。通常情况下,ReadWriterLockSlim用于管理并发访问,在写入时允许多个线程访问集合,只要没有进行写入操作,但在写入期间只允许一个线程(即写入者)访问。


1

来自http://msdn.microsoft.com/en-us/library/xfhwa508.aspx

字典可以支持多个读者并发访问,只要集合没有被修改。即便如此,枚举集合本质上不是线程安全的过程。在罕见的情况下,当枚举与写访问竞争时,必须在整个枚举期间锁定集合。为了允许多个线程对集合进行读写访问,您必须实现自己的同步。

对于线程安全的替代方案,请参见ConcurrentDictionary<TKey,TValue>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接