我的同事最近说,如果您不介意获取过期的数据,多个读写线程访问c#字典是可以的。他的理由是,由于程序会重复从字典中读取,因此过期的数据不会成为问题。
我告诉他,当您有一个编写器线程时,锁定集合始终是必要的,因为集合的内部状态将会被破坏。
我错了吗?
我的同事最近说,如果您不介意获取过期的数据,多个读写线程访问c#字典是可以的。他的理由是,由于程序会重复从字典中读取,因此过期的数据不会成为问题。
我告诉他,当您有一个编写器线程时,锁定集合始终是必要的,因为集合的内部状态将会被破坏。
我错了吗?
您是正确的,而您的同事是错误的:只有在没有写入器的情况下,才能从多个线程访问字典。
.NET 4.0 添加了 ConcurrentDictionary<K,T>
类,它恰好做了其名称所示的功能。
ConcurrentDictionary
可以加分,但仅依赖它并不足以真正理解其内部工作机制。Concurrent
集合提供了许多常见的多步骤进程的函数,将多个锁定操作转化为单一的管理进程,但它们并非万能替代品。 - Adam RobinsonTryGetValue
和 []
并不进行任何并发检查。它们有时会返回错误键的值而不是抛出异常,因此您将不知道要重试。 - Sergey KalinichenkoConcurrentDictionary
内部的锁定是系统瓶颈,还是这是一种过早的优化? - Sergey Kalinichenko你说得对,写入操作确实需要某种形式的锁定,但仅仅拥有写入权限并不意味着每次访问集合都必须使用lock() { }
。
正如你所说,内置集合的非同步版本仅在读取时是线程安全的。通常情况下,ReadWriterLockSlim
用于管理并发访问,在写入时允许多个线程访问集合,只要没有进行写入操作,但在写入期间只允许一个线程(即写入者)访问。
来自http://msdn.microsoft.com/en-us/library/xfhwa508.aspx:
字典可以支持多个读者并发访问,只要集合没有被修改。即便如此,枚举集合本质上不是线程安全的过程。在罕见的情况下,当枚举与写访问竞争时,必须在整个枚举期间锁定集合。为了允许多个线程对集合进行读写访问,您必须实现自己的同步。
对于线程安全的替代方案,请参见ConcurrentDictionary<TKey,TValue>
。