我试图在ConcurrentDictionary中使用AddOrUpdate方法。
从此页面上的“备注”部分https://msdn.microsoft.com/zh-cn/library/dd287191(v=vs.110).aspx。它说
“但是,这些方法的委托在锁之外调用,以避免在锁下执行未知代码可能引起的问题。因此,这些委托执行的代码不受操作原子性的约束。”
所以我不确定它是否是线程安全的。我有一种情况,如果找不到键,则值为1,否则将值增加1。
我写下面的功能
private static void AddOrUpdate(ConcurrentDictionary<string, int> map)
{
Random r = new Random();
Thread.Sleep(r.Next(10));
map.AddOrUpdate(Key, 1, (key, value) => value + 1);
}
public static void TestThreadSafe(ConcurrentDictionary<string, int> map)
{
Thread[] threads = new Thread[Size];
for (int i = 0; i < Size; ++i)
{
threads[i] = new Thread(() => AddOrUpdate(map));
}
foreach (var thread in threads)
{
thread.Start();
}
}
Run Code Online (Sandbox Code Playgroud)
创建了约300,000个线程并并行运行它们。结果始终为300,000。
以上方法线程安全吗?什么时候AddOrUpdate不安全线程?
使用时是线程安全的。当传递给的委托AddOrUpdate具有副作用时,它变得不是线程安全的,因为对于相同的键和现有值,这些副作用可能会执行两次。
例如:
private static void AddOrUpdate(ConcurrentDictionary<string, int> map)
{
Random r = new Random();
Thread.Sleep(r.Next(10));
map.AddOrUpdate(Key, 1, (key, value) => { Console.WriteLine(key + ": " + value); return value + 1; });
}
Run Code Online (Sandbox Code Playgroud)
可能会多次打印相同的键+值。
发生的情况是,有时ConcurrentDictionary可能会在多个线程上执行这些方法,然后获取结果值并输入锁以尝试应用它。其中一个线程将成功,另一个线程将进入锁,看到自读取值以来该值已更改,然后再次尝试委托。