多线程访问静态变量

时间:2017-11-09 14:48:44

标签: c# .net multithreading thread-safety locking

我正在研究.net中的多线程,我坚持这个例子:

    class Program
    {
        public static ConcurrentDictionary<string, int> Channels;


        static void Main(string[] args)
        {
            Channels = new ConcurrentDictionary<string, int>();


            Thread adding = new Thread(AddItem);
            Thread reading = new Thread(CountItems);

            reading.Start();
            adding.Start();

            Console.ReadKey();
        }

        public static void AddItem()
        {
            int i = 0;
            while (true)
            {
                i++;
                Channels.TryAdd(i.ToString(), i);
                Console.WriteLine(i + ": Item Added");

                Thread.Sleep(2000);
            }
        }

        public static void CountItems()
        {
           while (true)
           {
              Console.WriteLine(Channels.Count);
              Thread.Sleep(3000);
           }
        }    
    }

输出是:

1: Item Added
1
2: Item Added
2
3: Item Added
4: Item Added
4

问题是,为什么这两个线程在没有Lock的情况下工作?不应该是那个阅读线程不应该看到变化吗?

Source 1

Source 2

2 个答案:

答案 0 :(得分:1)

首先,有一个锁,但它在ConcurrentDictionary

接下来,检查一下锁的含义。这意味着没有2个线程可以进入锁定,在这种情况下,在确切的时间瞬间访问字典。

这并不意味着每个线程都看不到其他线程可以看到的内容。两个线程都看到完全相同的对象引用,因此两者都访问相同的数据,而不是完全相同的时间。锁只在Count或TryAdd的调用中。

要了解锁定的重要性,请查看最简单的示例:

i = i + 1;

这看起来像1个语句,但它将被编译为许多机器代码指令,如下所示:

  1. 加载i的内存值
  2. 添加1
  3. 将新值mack存储到内存中
  4. 当你有多个线程时,这些机器插件是交错在一起的,而不是C#。为什么这很重要?好吧,如果您有2个线程同时执行上述语句,那么可能会发生这种情况:

    想象我= 42

    1. 线程1从mem(42)
    2. 加载i的值
    3. 线程2从mem(仍然是42)
    4. 加载i的值
    5. 线程1添加1并将结果存储在内存(现在为43)
    6. 线程2也是如此(它也存储了43个)
    7. 所以,有2次尝试增加i,但结果是它只增加了一次。

      现在想象一下字典类中可能会发生什么 - 可能还有更多只是增加一个计数器 - 如果2个线程同时执行某些操作,则无法确定会发生什么。

      现在,如果你这样做:

      lock (someObject) 
      {
          i = i + 1;
      }
      

      这意味着{}同时只允许1个线程。如果一个线程在{}中,而其他线程想要进入,它就会等到第一个线程完成。

答案 1 :(得分:0)

  

问题是,为什么这两个线程没有锁定?

ConcurrentDictionary有自己的内部锁。

  

不应该是阅读线程不应该看到变化吗?

为什么呢?两个线程都使用相同的静态字典实例。

<强>更新

如果您将ConcurrentDictionary更改为Dictionary,则输出将保持不变。你应该非常幸运(或者说不幸的是)用这么简单的应用程序来解决一些问题。但是,如果你从许多不同的线程使用Dictionary,特别是如果这些线程修改了字典,你就不能确定你不会破坏内部字典数据或得到一些奇怪的例外。

相关问题