【发布时间】:2017-02-27 02:51:01
【问题描述】:
我正在尝试拥有一个 keys 的集合,如果密钥不存在,那么我需要 DoSomeMethod() 然后将密钥添加到集合中。
问题是,这需要能够同时处理多个线程尝试添加相同的键。
如果两个线程有相同的键,那么只有一个会做DoSomeMethod(),而另一个需要等待。
我已经研究过使用ConcurrentDictionary 和GetOrAdd (with the Func(..) param option) 方法,但如果两个线程具有相同的密钥,这似乎会同时“启动”。我以为GetOrAdd 的实现会是
- 锁定“钥匙”
- 从
key获取value。 - 如果没有
value然后做任何事情.. 现在设置值。 - 返回
value.
... 和任何其他key命中将等到锁定完成。
感觉就像GetOrAdd 方法调用的我的自定义方法不是线程安全的。
MSDN 文档也建议这样做?
备注
如果在不同线程上同时调用GetOrAdd,addValueFactory可能会被多次调用,但它的键/值对可能不会每次调用都添加到字典中
悔过的例子:我正在将文件从source 复制到destination。
- 复制
source文件时,如果我们尝试检查并创建目标文件夹,请检查集合。 - 如果集合没有
key,则检查目标文件夹是否不存在...如果不存在则创建它。 - 创建目标文件夹后,将此文件夹名称/路径存储在集合中。
- 对所有文件重复。
所以实际上,如果我们还没有创建目标文件夹,我们只会创建它。
这就像我想锁定一个集合 KEY ....
【问题讨论】:
-
您必须在同一个锁中结合“检查”和“执行”。如果一个线程检查密钥并决定做某事,那么线程可以中断“做”并尝试做同样的事情。我是正确理解问题还是有其他问题?
-
不——第二个线程不能中断,但必须等待。
-
我还是不明白问题出在哪里。为什么并发字典不适合你?
-
是的,这就是 GetOrAdd 的工作方式。它不保证值工厂不会被调用两次,只是保证您在两个线程上获得相同的值。您建议的实现的问题是它需要一个相当高级别的锁(每次检查一个键)并且它需要在调用外部代码时保持一个锁。并发集合是“低锁”以在高争用场景中最大化吞吐量。在持有锁的同时调用外部代码是不好的,因为它会为死锁创建路径。
-
你可以选择使用 SemaphoreSlim 和超时吗?你也可以检查一下。
标签: c# .net multithreading collections thread-safety