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

Should I use a lock object while Cloning a ConcurrentDictionary in C#?

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


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

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

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

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