架构师_程序员_码农网

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 17046|回复: 7

[资料] Volatile vs. Interlocked vs. lock

[复制链接]
发表于 2018-8-27 14:40:35 | 显示全部楼层 |阅读模式
假设有一个类,它含有一个可以被多线程访问的public int counter 字段, 这个数字只会增加或减少。

当去增加这个字段的时候,应该采用下面哪个方案,为什么?

  • lock(this.locker) this.counter++;
  • Interlocked.Increment(ref this.counter);
  • Change the access modifier of counter to public volatile


最糟糕(实际上都不能起作用)

Change the access modifier of counter to public volatile

这种方式实际上根本不安全,关于volatile的重点是:运行在多个CPU上的多个线程会缓冲数据和重新排列执行的指令。

如果它是非volatile, 当CPU A增加了一个值, CPU B需要等一会才能看到增加的值,这可能导致问题的出现。

如果它是volatile,就能确保两个CPU能在相同的时间看到相同的值。但它并不能避免交叉的读写操作。

给一个变量增加值,实际上需要三步

1. 读, 2. 增加 3.写

假设线程A 读取 counter 的值为1,还没有准备增加,这时线程B也读取counter 的值为1, 然后两个线程都开始执行增加,写的操作。最终counter 的值为2. 这是不对的,两个线程都做了增加操作,正确的结果应该是3. 所以把它标志为volatile根本就是不安全的。

比较好

lock(this.locker) this.counter++;

这种方式是安全的(当然你要记得lock所有你想访问this.counter的地方)。它防止其它任何线程去执行被lock锁住的代码。并且它还能防止上面提到的多CPU指令排序的问题。但问题是,lock在性能上比较慢,并且如果你在其它的一些不相干的地方也用了lock,可能会导致阻塞你的其它线程。

最好

Interlocked.Increment(ref this.counter);

这个是安全的,也是非常高效的。它执行读,增加,写三个操作在一个原子中,不会在中间被打断。因为它不影响其它的代码,你也不需要记住其它地方的Lock. 并且它还非常快(正如MSDN说的,在现在的CPU上,它常常只是一个指令)。

但是我不完全确定它能否也能解决CPU的指令排序的问题,还是需要结合volatile和这个increment一起使用。

补充:到底volatile是擅长解决什么问题的?

既然volatile不能防止多线程的问题,那它能干啥用?举个很好的例子,假设你有两个线程,一个总是往一个变量中进行写操作,假设这个变量为queneLength, 另一个总是从这个变量中读取数据。如果queueLenght不是volatile的, 线程A可能读了5次,但是线程B可能看到的是延迟的数据,甚至可能看到错误的顺序的数据。一个方案就是用lock, 但是在这种情况系你也可以用volatile. 这样能确保线程B总能看到线程A写的最新数据,但是这个这个逻辑仅仅在你写的时候没有读,读的时候没有写时才有效。一旦多个线程都要做读-修改-写的操作,你需要用Interlocked或者用lock.





上一篇:Java MD5加密方法
下一篇:Linq 实现sql中的not in和in条件查询
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2018-8-29 16:21:42 | 显示全部楼层
你好,前面您做的那个highcharts 堆叠柱状图和可下钻的柱状图结合起来的代码?我想问问你能否发一个https://jshare.com.cn/demos/hhhhDz?hc-theme=grid-light界面上可以编辑的代码?
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2018-8-29 16:22:13 | 显示全部楼层
全网就你这一个资源。
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2018-8-29 16:22:55 | 显示全部楼层
因为之前那个90以前的不能留言了。so
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2018-8-29 16:40:40 | 显示全部楼层
方便加个联系方式吗?咨询下您
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
 楼主| 发表于 2018-9-15 13:10:16 | 显示全部楼层
private static int safeInstanceCount = 0;//使用原子操作
System.Threading.Interlocked.Increment(ref safeInstanceCount); 自增加1
System.Threading.Interlocked.Decrement(ref safeInstanceCount); 自减1
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2020-5-21 16:43:54 | 显示全部楼层
支持楼主分享
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
 楼主| 发表于 2020-6-11 15:00:16 | 显示全部楼层
一、概念

  在多线程环境中,不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

二、类

  System.Threading.Interlocked 静态类

三、常用函数(其他的自己看吧)

1.public static int Decrement(ref int location); //以原子操作的形式递减指定变量的值并存储结果

相当于 lock(obj){i--;}

2.public static int Increment(ref int location); //以原子操作的形式递增指定变量的值并存储结果

相当于 lock(obj){i++;}
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

免责声明:
码农网所发布的一切软件、编程资料或者文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:help@itsvse.com

QQ|手机版|小黑屋|架构师 ( 鲁ICP备14021824号-2 )|网站地图

GMT+8, 2024-4-25 12:12

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表