【问题标题】:Is AddOrUpdate thread safe in ConcurrentDictionary?ConcurrentDictionary 中的 AddOrUpdate 线程是否安全?
【发布时间】:2016-08-26 00:40:04
【问题描述】:

我尝试在 ConcurrentDictionary 中使用 AddOrUpdate 方法。

来自此页面上的“备注”部分https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx。它说

“但是,这些方法的委托在锁之外调用以避免在锁下执行未知代码可能出现的问题。因此,这些委托执行的代码不受操作的原子性。”

所以我不确定它是否是线程安全的。我有一种情况,如果没有找到key,则值为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();
        }
    }

创建了大约 300,000 个线程并并行运行它们。结果总是 300,000。

上面的方法线程安全吗? AddOrUpdate 何时不是线程安全的?

【问题讨论】:

  • 线程安全与否,尝试创建 300,000 个线程(您将遇到max thread count waay before then)对于您的场景可能不是一个好主意。 Parallel.ForEach 更友好

标签: c# multithreading


【解决方案1】:

在您的使用中它是线程安全的。当传递给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; });
    }

可以多次打印同一个键 + 值。

发生的情况是,有时 ConcurrentDictionary 可能会在多个线程上执行这些方法,然后获取结果值并输入锁以尝试应用它。一个线程会成功,然后另一个线程会进入锁,看到值在读取后发生了变化,然后再次尝试委托。

【讨论】:

  • 一个例子是dict.AddOrUpdate(..., ..., (k, v) =&gt; v.Add(item));,其中字典包含一个普通的通用列表,并且可能会将该项目两次添加到引用类型中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多