在C#中克隆ConcurrentDictionary时应该使用锁对象吗?

时间:2019-04-16 07:40:45

标签: c# collections thread-safety clone concurrentdictionary

我有一个代码库,其中多个线程正在ConcurrentDictionary中写入,每60秒另一个线程运行并克隆主CD,清除它,然后继续在克隆的CD上工作。我想知道如果在lock主CD上不使用Cloning and Clearing会丢失一些数据吗?演示此问题的代码如下:


class Program
    {
        static object lock_obj = new object();

        static async Task Main(string[] args)
        {
            ConcurrentDictionary<string, ThreadSafeLong> cd = new ConcurrentDictionary<string, ThreadSafeLong>();


            Func<Task> addData = () =>
            {
                return Task.Run(async () =>
               {
                   var counter = 1;

                   while (true)
                   {
                       lock (lock_obj)
                       {
                           for (int i = 0; i < 100_000; i++)
                           {
                               cd.TryAdd($"{counter}:{i}", new ThreadSafeLong(i));
                               //WriteLine(i);
                           }
                           WriteLine($"Round {counter}");
                       }
                       counter++;
                       await Task.Delay(1_000);
                   }
               });
            };

            Func<Task> writeData = () =>
            {
                return Task.Run(async () =>
              {
                  while (true)
                  {
                      var sw = Stopwatch.StartNew();

                      lock (lock_obj) // to clone the data, and prevent any other data to be added while clone
                      {
                          var cloned = new ConcurrentDictionary<string, ThreadSafeLong>(cd);
                          cd.Clear();
                          WriteLine($"Cloned Count: {cloned.Count}");
                      }

                      sw.Stop();
                      WriteLine($"Elapsed Time: {sw.ElapsedMilliseconds}");

                      await Task.Delay(6_000);
                  }
              });
            };

            await Task.WhenAll(addData(), writeData());

        }
    }

PS::某种程度上可能与问题here

有关

1 个答案:

答案 0 :(得分:3)

在这些情况下,我将字典替换为新字典,而不是调用clear:

lock (lock_obj)
{
    var cloned = cd;
    cd = new ConcurrentDictionary<string, ThreadSafeLong>();
}

在这种情况下,其他线程完成了对旧线程的写入或已经在使用新线程。