对于Dictionary<TKey, TValue>,假设您将实现自己的逻辑以确保不输入重复的键。例如,
if(!myDictionary.ContainsKey(key)) myDictionary.Add(key, value);
但是当我们有多个线程在运行并且它们可能同时尝试修改字典时,我们会使用并发集合。
如果两个线程尝试同时执行上述代码,myDictionary.ContainsKey(key) 可能会为两个线程返回 false,因为它们都在同时检查并且尚未添加该键。然后他们都尝试添加密钥,但其中一个失败了。
阅读该代码但不知道它是多线程的人可能会感到困惑。我检查以确保该键不在字典中在我添加它之前。那么我如何获得异常呢?
ConcurrentDictionary.TryAdd 通过允许您“尝试”添加密钥来解决这个问题。如果添加该值,则返回true。如果不是,则返回false。但它不会做的是与另一个 TryAdd 冲突并抛出异常。
您可以通过将Dictionary 包装在一个类中并在其周围放置lock 语句来自己完成所有这些操作,以确保一次只有一个线程进行更改。 ConcurrentDictionary 只是为您做到这一点并且做得非常好。您不必查看其工作方式的所有详细信息 - 您只需知道已考虑到多线程即可使用它。
这是在多线程应用程序中使用类时要注意的细节。如果您转到ConcurrentDictionary Class 的文档并滚动到底部,您会看到:
线程安全
所有公共和受保护成员
ConcurrentDictionary 是线程安全的,可以使用
同时来自多个线程。但是,通过访问的成员
ConcurrentDictionary 的接口之一
不保证实现,包括扩展方法
线程安全,可能需要由调用者同步。
换句话说,多个线程可以安全地读取和修改集合。
在Dictionary Class 下你会看到:
线程安全
一个字典可以支持多个
读者并发,只要集合没有被修改。甚至
因此,通过集合枚举本质上不是
线程安全的过程。 在极少数情况下枚举争用
对于写访问,集合必须在整个过程中被锁定
枚举。允许多个人访问该集合
读写线程,你必须实现你自己的
同步。
多个线程可以读取键,但如果多个线程要写入,那么您需要以某种方式lock 字典以确保一次只有一个线程尝试更新。
Dictionary<TKey, TValue> 公开了一个Keys 集合和Values 集合,因此您可以枚举键和值,但如果另一个线程要修改字典,它会警告您不要尝试这样做。在添加或删除项目时,您无法枚举某些内容。如果您需要遍历键或值,则必须锁定字典以防止在该迭代期间更新。
ConcurrentDictionary<TKey, TValue> 假定将有多个线程读写,因此它甚至不会公开键或值集合供您枚举。