无锁代码下,在读写字段时使用内存屏障往往是不够的。在 64 位字段上进行加、减操作需要使用Interlocked工具类这样更加重型的方式。Interlocked也提供了ExchangeCompareExchange方法,后者能够进行无锁的读-改-写(read-modify-write)操作,只需要额外增加一点代码。

如果一条语句在底层处理器上被当作一个独立不可分割的指令,那么它本质上是原子的(atomic)。严格的原子性可以阻止任何抢占的可能。对于 32 位(或更低)的字段的简单读写总是原子的。而操作 64 位字段仅在 64 位运行时环境下是原子的,并且结合了多个读写操作的语句必然不是原子的:

class Atomicity
{static int _x, _y; static long _z; static void Test() { long myLocal; _x = 3; // 原子的 _z = 3; // 32位环境下不是原子的(_z 是64位的) myLocal = _z; // 32位环境下不是原子的(_z 是64位的) _y += _x; // 不是原子的 (结合了读和写操作) _x++; // 不是原子的 (结合了读和写操作) } } 

在 32 位环境下读写 64 位字段不是原子的,因为它需要两条独立的指令:每条用于对应的 32 位内存地址。所以,如果线程 X 在读一个 64 位的值,同时线程 Y 更新它,那么线程 X 最终可能得到新旧两个值按位组合后的结果(一个撕裂读(torn read))。

编译器实现x++这种一元运算,是通过先读一个变量,然后计算,最后写回去的方式。考虑如下类:

class ThreadUnsafe
{static int _x = 1000; static void Go() { for (int i = 0; i < 100; i++) _x--; } } 

抛开内存屏障的事情,你可能会认为如果 10 个线程并发运行Go,最终_x会为0。然而,这并不一定,因为可能存在竞态条件(race condition),在一个线程完成读取x的当前值,减少值,把值写回这个过程之间,被另一个线程抢占(导致一个过期的值被写回)。

当然,可以通过用lock语句封装非原子的操作来解决这些问题。实际上,锁如果一致的使用,可以模拟原子性。然而,Interlocked类为这样简单的操作提供了一个更方便更快的方案:

class Program
{static long _sum; static void Main() { // _sum // 简单的自增/自减操作: Interlocked.Increment (ref _sum); // 1 Interlocked.Decrement (ref _sum); // 0 // 加/减一个值: Interlocked.Add (ref _sum, 3); // 3 // 读取64位字段: Console.WriteLine (Interlocked.Read (ref _sum)); // 3 // 读取当前值并且写64位字段 // (打印 "3",并且将 _sum 更新为 10 ) Console.WriteLine (Interlocked.Exchange (ref _sum, 10)); // 10 // 仅当字段的当前值匹配特定的值(10)时才更新它: Console.WriteLine (Interlocked.CompareExchange (ref _sum, 123, 10); // 123 } } 

Interlocked上的所有方法都使用全栅栏。因此,通过Interlocked访问字段不需要额外的栅栏,除非它们在程序其它地方没有通过Interlockedlock来访问。

Interlocked的数学运算操作仅限于IncrementDecrement以及Add。如果你希望进行乘法或其它计算,在无锁方式下可以使用CompareExchange方法(通常与自旋等待一起使用)。我们会在并行编程中提供一个例子。

Interlocked类通过将原子性的需求传达给操作系统和虚拟机来进行实现其功能。

Interlocked类的方法通常产生 10ns 的开销,是无竞争锁的一半。此外,因为它们不会导致阻塞,所以不会带来上下文切换的开销。然而,如果在循环中多次迭代使用Interlocked,就可能比在循环外使用一个锁的效率低(不过Interlocked可以实现更高的并发度)。

转载于:https://www.cnblogs.com/kzwrcom/p/5392141.html

C# Interlocked 笔记相关推荐

  1. 多线程笔记--原子操作Interlocked系列函数

    前面写了一个多线程报数的功能,为了描述方便和代码简洁起见,只输出最后的报数结果来观察程序运行结果.这非常类似一个网站的客户访问统计,每个用户登录用一个线程模拟,线程运行时将一个表示计数的变量递增.程序 ...

  2. 【汇编语言与计算机系统结构笔记17】MIPS 汇编初步

    本次笔记内容: 25.MIPS汇编初步-1 26.MIPS汇编初步-2 27.MIPS指令集与汇编程序设计 注:我找到了对应内容的课件,请见我于GitHub的CS笔记仓库.因此,为了节省时间,我只记录 ...

  3. 现代C++教程笔记(下)

    文章目录 前言 第 6 章 正则表达式 第 7 章 并行与并发 额外可参考的资料 并行基础 互斥量与临界区 期物 条件变量 原子操作与内存模型 原子操作 一致性模型 内存顺序 第 8 章 文件系统 第 ...

  4. C++Windows核心编程读书笔记(转)

    http://www.makaidong.com/(马开东博客) 这篇笔记是我在读<windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的 ...

  5. [C++]《Windows核心编程》读书笔记

    这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入 ...

  6. 【读书笔记】知易行难,多实践

    前言: 其实,我不喜欢看书,只是喜欢找答案,想通过专业的解答来解决我生活的困惑.所以,我听了很多书,也看了很多书,但看完书,没有很多的实践,导致我并不很深入在很多时候. 分享读书笔记: <高效1 ...

  7. 【运维学习笔记】生命不息,搞事开始。。。

    001生命不息,搞事不止!!! 这段时间和hexesdesu搞了很多事情! 之前是机械硬盘和固态硬盘的测速,我就在那默默的看着他一个硬盘一个机械测来测去. 坐在他后面,每天都能看到这位萌萌的小男孩,各 ...

  8. SSAN 关系抽取 论文笔记

    20210621 https://zhuanlan.zhihu.com/p/353183322 [KG笔记]八.文档级(Document Level)关系抽取任务 共指id嵌入一样 但是实体嵌入的时候 ...

  9. pandas以前笔记

    # -*- coding: utf-8 -*- """ Created on Sat Jul 21 20:06:20 2018@author: heimi "& ...

最新文章

  1. Go 1.12发布:改进了运行时性能以及模块支持
  2. java面试-深入理解JVM(一)——JVM内存模型
  3. Cortex-M3 异常中断向量表
  4. C# 把特定数字移动到数字前面,其他顺序不变。
  5. 汇编语言--不可屏蔽中断
  6. linux命令join与paste
  7. NOI前总结:点分治
  8. linux实验试题 cp,51CTO博客-专业IT技术博客创作平台-技术成就梦想
  9. 2020数学建模国赛C题思路
  10. redis数据类型list总结
  11. 矩阵的最大路径和问题
  12. 7月20日专家在线访谈“员工上网管理是否可行”
  13. 【优化算法】多目标粒子群优化算法(MOPSO)【含Matlab源码 033期】
  14. 测试宝宝照片的软件,未来宝宝照片合成器
  15. html思维导图word版,[精选]思维导图(完美排版word).doc
  16. 在线播放m3u8和ts的方法
  17. A+B的各种写法(不是couta+b;)
  18. Telemetry 标准日志接口如何提升运维效率?
  19. dede织梦后台页面及功能修改及精简操作方法
  20. 一次培训机构的Java面试

热门文章

  1. 四、Flash Media Server3.5安全特性
  2. 十大编程算法助程序员走上高手之路
  3. 微博深度学习平台架构和实践
  4. windows下实现Git在局域网使用
  5. 类似QQ管家页面jquery图片显隐轮换效果
  6. ERP项目选型实施注意的几点(二)
  7. 兄弟||弟兄,以前看过一个“母亲”,现在是“弟兄”,感人!
  8. Sub-process /usr/bin/dpkg returned an error code (1) 如何解决
  9. Android环信爬坑指北(二)头像昵称好友备注显示
  10. 2018区块链生存指南:要做飞行的猪、摔不坏的弹球、未来的种子