C# 字典迭代 - 意外结果

本文关键字:意外 结果 迭代 字典 | 更新日期: 2023-09-27 18:34:29

做了一个小例子,为多线程环境中频繁的字典更新选择最佳方案。然后观察到"奇怪"的迭代行为。

using System;
using System.Collections.Generic;
using System.Threading;
namespace DictionaryTest
{
    class Program
    {
        static int _n;
        static Dictionary<int, string> _dict = new Dictionary<int, string>();
        static void Main(string[] args) {
            for (int i = 0; i < 50; i++) _dict[i] = "FIRST";
            new Thread(ReadDict).Start();
            Thread.Sleep(30);
            // CASE A, Throws exception AS EXPECTED.
            //_dict.Clear();
            // CASE B, ReadDict continues iterate on old dictionary values!!!???
            //_dict = new Dictionary<int, string>();
            // CASE C
            UpdateDict();
            ReadDict();
            Console.ReadKey();
        }
        private static void ReadDict() {
            // Read Method X
            // (continues iterate on old dictionary values!!!???)
            //
            foreach (var kvp in _dict) {
                Thread.Sleep(3);
                Console.WriteLine("{0,3} {1,4} {2,6} :{3,5} Method X", Interlocked.Increment(ref _n), kvp.Key, kvp.Value, Thread.CurrentThread.ManagedThreadId);
            }
            // Read Method Y
            //
            //for (int i = 0; i < 50; i++) {
            //    Thread.Sleep(3);
            //    Console.WriteLine("{0,3} {1,4} {2,6} :{3,5} Method Y", Interlocked.Increment(ref _n), i, _dict[i], Thread.CurrentThread.ManagedThreadId);
            //}
        }
        private static void UpdateDict() {
            var tmp = new Dictionary<int, string>();
            for (int i = 0; i < 50; i++) tmp[i] = "SECOND";
            _dict = new Dictionary<int, string>(tmp);
        }
    }
}

组合:

  1. 案例 A 和(方法 X 或方法 Y( - 按预期引发异常!
  2. 案例 B 和方法 X - 继续迭代旧的字典值???
  3. CASE C 和方法 X - 继续迭代旧的字典值???
  4. 案例 C 和方法 Y - 仅按预期迭代更新的值!

循环foreach是否拍摄静态字典成员的某种内部快照?如果是这样,案例 A 也应该工作,但它没有。
有人可以解释是什么导致了这种奇怪的行为吗?

编辑:

尽管使用了 ConcurrentDictionary 和代码锁定,但基本上我的问题是关于字典更新方法之间的差异:将字典的新副本分配为全新的对象,或者更好地迭代某些集合并使用 dict[key]=myObject 方法单独更新新值?我不需要保留对值对象的引用,只需替换它即可。

C# 字典迭代 - 意外结果

字典不是线程安全的。 对于线程安全字典,请使用

ConcurrentDictionary<int, string>

它可从命名空间 System.Collections.Concurrent;

请参阅以下关于并发词典的优秀教程。

http://arbel.net/2013/02/03/best-practices-for-using-concurrentdictionary/

编辑:

我相信问题的一部分是,即使使用并发字典,您仍然不是线程安全的,因为您的更新方法没有使用并发字典线程安全操作方法。 以下内容尊重集合上的锁定。

private static void UpdateDict()
{
    for (int i = 0; i < 50; i++)
    {
         _dict.AddOrUpdate(i, _ => "SECOND", (i1, s) => "SECOND");
    }    
}

您应该发现这保留了字典。 我在索引器上有一个错字。 现在,您可以按照您的预期正确读取。