首页 新闻 会员 周边

请教一个关于C#中的volatile变量的问题....

0
悬赏园豆:10 [已解决问题] 解决于 2016-04-22 16:56

以前的理解,用volatile 关键字 修饰的变量, 可以在多个线程之间同步最新的值.

当A线程更改值后,B线程马上可以获取到.

我这样的理解对吗?

 

但是使用的过程中,发现并不是这样的,某些线程修改的值会被忽略掉..

简单的Demo如下:static volatile int callCount = 0;

 

 

      static object ob = new object();
        static void ThreadTest()
        {
            ++callCount;
            Console.WriteLine("CallCount="+callCount);
        }
        static volatile int callCount = 0;
        static void Main(string[] args)
        {
            for (int i = 0; i < 1000; i++)
            {
                Thread t = new Thread(new ThreadStart(() => { ThreadTest(); }));
                t.Start();
            }

            Console.WriteLine("等一段时间让后台线程跑完");

            Thread.Sleep(10000);

            Console.WriteLine("最终被调用的次数为:" + callCount);
            Thread.Sleep(10000);
            Console.Read();
        }

 

直接在VS里按F5运行的效果如下:

 

 

为什么?本来应该是1000才对的啊.

 

可是在生成的bin目录下,双击运行,却始终是1000

 

这是为什么呢?

hexllo的主页 hexllo | 菜鸟二级 | 园豆:318
提问于:2016-04-22 14:09
< >
分享
最佳答案
0

volatile并不保证线程安全性,它只保证任何时候你读取到的都是最新值,你int的加法又不是原子性操作。

这块你要换成Interlocked.Increment/Decrement(cas操作),并去掉volatile(没意义了,cas内部保障)。

ps:volatile性能并不好,很多时候你可以继续优化,比如

int Read(){

Thread.MemoryBarrier();

return x;

}

 

收获园豆:6
Daniel Cai | 专家六级 |园豆:10424 | 2016-04-22 16:02
其他回答(2)
0

将你的代码改装下

        static object ob = new object();
        static void ThreadTest()
        {
            ++callCount;
            Console.WriteLine("CallCount=" + callCount);
            lock (ob)
            {
                counter++;
            }
        }

        static int counter = 0;
        static volatile int callCount = 0;
        static void Main(string[] args)
        {
            for (int i = 0; i < 1000; i++)
            {
                Thread t = new Thread(new ThreadStart(() => { ThreadTest(); }));
                t.Start();
            }

            Console.WriteLine("等一段时间让后台线程跑完");

            Thread.Sleep(10000);

            Console.WriteLine("最终被调用的次数为:" + callCount);
            Console.WriteLine("counter:" + counter);
            Thread.Sleep(10000);
            Console.Read();
        }

因为并发的时候,重复计数了,算做+1

收获园豆:2
上帝之城 | 园豆:2549 (老鸟四级) | 2016-04-22 14:45

楼主测试的是volatile关键字。不是锁。。

支持(0) 反对(0) waiter | 园豆:1000 (小虾三级) | 2016-04-22 15:00

@waiter: 看文字

支持(0) 反对(0) 上帝之城 | 园豆:2549 (老鸟四级) | 2016-04-22 15:11

@上帝之城: 楼主问的是深层次的原因, 你一个lock把counter++的取值、计算、赋值三步操作都个锁住了,问题的原因就在于多线程中这三步会发生很多交叉执行~

支持(0) 反对(0) waiter | 园豆:1000 (小虾三级) | 2016-04-22 15:32
0

volatile只是保证你每次读取的值都是最新的,我的猜测是 debug情况下,出现多次thread同时读取该值的概率相对大。所以存在多个线程在执行++callCount操作时,callCount是一样的。

收获园豆:2
waiter | 园豆:1000 (小虾三级) | 2016-04-22 15:14

++callCount表示 callCount=callCount+1;

第一步,callCount+1时做取值操作; 这一步volatile保证所有线程取的都是最新的。。

第二步,callCount=??时做赋值操作;这一步就是个赋值操作,volatile保证你的赋值操作正确。

因为是多线程,所以你前脚存了801,我后脚存了800.

支持(0) 反对(0) waiter | 园豆:1000 (小虾三级) | 2016-04-22 15:26

@waiter: 谢谢..我以为有了volatile后,就变成原子操作了..原来不是的,和楼下说的一样.

支持(0) 反对(0) hexllo | 园豆:318 (菜鸟二级) | 2016-04-22 16:57
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册