静态变量的并发增量 g = g + 1 看起来是原子的,但不应该是?

Concurrent increment g = g + 1 of a static variable appears atomic, but shouldn't be?

无法找到为什么下面带有注释 Monitor 的代码在写入 g 时像同步一样工作并且总是 returns g=50。我期待一些差异结果小于 50

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace _7_monitor
{
    class Program
    {
        static object sync = new object();

        static int g = 0;

        static Barrier b = new Barrier(6, (b) => { Console.WriteLine("barier reached"); } );

        public static void tjob(object obj)
        {
            int t = (int)obj;

            for (int i = 0; i < 10; i++)
            {
                //Monitor.Enter(sync);
                g = g + 1;
                //Monitor.Exit(sync);

                Console.WriteLine("thr {0} iter={1}", t ,  i);
            }
            b.SignalAndWait();
        }

        static void Main(string[] args)
        {

            for (int i = 0; i < 5; i++)
            {
                Thread d = new Thread(tjob);
                d.Start(i);
            }

            Console.WriteLine("waiting");
            b.SignalAndWait();

            Console.WriteLine("g={0}",g);
            Console.ReadLine();

        }
    }
}

正如其他人指出的那样,一个 10 的循环会很快完成,以至于其他线程很可能还没有开始,所以您可能已经顺序访问了全局静态变量 g,无论如何,因此观察到的结果是一致的。

在没有围绕 g 的保护的情况下,使用更长的循环(我去掉了一些多余的部分),我们确实得到了类似于随机数生成器的东西。

var threads = new List<Thread>();
for (var i = 0; i < 5; i++)
{
    var d = new Thread(x => {
        for (var loop = 0; loop < 100000; loop++)
        {
            // unsynchronized mutation
            g = g + 1;
        }
    });
    d.Start(i);
    threads.Add(d);
}
foreach (var t in threads)
{
    t.Join();
}

Console.WriteLine("g={0}", g); // 158609, 173331, 127983, ... (i7 with 4 HT Cores)

根据@Jurgis 的评论,Interlocked.Increment 已提供给 .Net

而不是 Monitorlock

g = g + 1 替换为 Interlocked.Increment(ref g); returns 预期:

g=500000

(显然,在现实世界中,并行化只会争用共享变量的工作绝对没有意义)