首页 > 解决方案 > 静态变量的并发增量 g = g + 1 看起来是原子的,但不应该是?

问题描述

找不到为什么下面带有注释的代码可以Monitor在写入时同步g并始终返回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();

        }
    }
}

标签: c#monitor

解决方案


正如其他人所指出的那样,一个 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 而不是or提供Monitor给.Netlock

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

g=500000

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


推荐阅读