只有真正理解这个概念,才能理解多线程的核心。单个线程的重排可以通过原子操作搞定,但多个线程之间的原子操作没有内在的关系,相当于是两个自治的操作序列,或者说两个局部时间域。如何将这两个时间的某些操作强行规定一个先后关系,而其他操作不管,就是synchronize-with所意味的。

那么如何做同步呢?就是引入一个点,可以成为同步点,这个点是两个线程的参照物,一个线程的一些操作必须在这个点之前完成,另一个线程的操作必须在这个点之后完成。在代码中就是某个操作成为这个同步点,如何让它成为同步点?

就是tagged,打标记。而标记的设计也是成体系的。对应上面说的,memory_order_release的标记就代表前面的操作必须在这个点之前完成,对于其他的操作不管:比如后面的是不是跑到前面完成了。而memory_order_acquire就是这个点后面的操作必须在点后面完成,就刚好禁止了后面的是不是跑到前面完成了这种情况。

synchronize-with

能否翻译一下下面这段出自于《c++ concurrency in action》中关于C++内存模型的描述?

The basic idea is this: a suitably tagged atomic write operation W on a variable x syn-chronizes-with a suitably tagged atomic read operation on x that reads the value stored by either that write ( W ), or a subsequent atomic write operation on x by the same thread that performed the initial write W , or a sequence of atomic read-modify-write operations on x (such as fetch_add() or compare_exchange_weak() ) by any thread, where the value read by the first thread in the sequence is the value written by W(see section 5.3.4).

Back in section 5.3.1, I mentioned that you could get a synchronizes-with relationship between a store to an atomic variable and a load of that atomic variable from another thread, even when there’s a sequence of read-modify-write operations between the store and the load, provided all the operations are suitably tagged. Now that I’ve covered the possible memory-ordering “tags,” I can elaborate on this. If the store istagged with memory_order_release, memory_order_acq_rel, or memory_order_seq_cst, and the load is tagged with memory_order_consume, memory_order_acquire,or memory_order_seq_cst, and each operation in the chain loads the value written by the previous operation, then the chain of operations constitutes a release sequence and the initial store synchronizes-with (for memory_order_acquire or memory_order_seq_cst) or is dependency-ordered-before (for memory_order_consume) the final load. Any atomic read-modify-write operations in the chain can have any memory ordering (even memory_order_relaxed).

上面那段话的基本意思是:如果有一个线程A对一个atomic对象调用了write操作,假设使用的是std::memory_order_release,而线程B对这个atomic对象调用了read操作,假设使用的是std::memory_order_acquire,那么即使线程A在之前的那个write之后,在线程B的read之前有一个使用std::memory_order_relaxed的写操作,也不会影响没有这个write操作建立的同步(synchronize-with)关系,而且线程B读到的是线程A第二次write的值。如果期间存在线程对这个atomic对象调用读写操作,那么线程A的写操作(使用std::memory_order_release)与线程B的读取操作(使用std::memory_order_acquire)仍然存在同步(synchronize-with)关系,而且中间的读写操作读取的是线程A写的值,线程B读取的是中间读写操作写的值,即使中间读写操作使用的std::memory_order_relaxed。当然,写操作和读操作不是一定需要用上面说的两种内存序(memory_order),可以选择其他能够建立同步(synchronize-with)关系的内存序。

比如consume就是dependency-ordered-before。

section5.3.1解释的是synchronizes-with这个概念,我的理解就是解释两个CPU操作(单线程内或者多线程内)在什么情况下算是“同步”的。首先要知道程序在编译、执行过程中可能发生reorde——即指令重排。CPU实际的指令执行顺序称为“memory ordering”。在memory ordering中,有相邻的两个操作write x/read x(当然,中间也可以有对其他变量的操作),那么,read到的x一定是write到的值,这就是“同步”。你要问,这不是很显然的吗?请搜索“CPU高速缓存一致性”的相关介绍,简单来说,如果没有CPU缓存一致性,上述两个操作就可能得不到正确的结果。内存模型均以CPU缓存一致性模型为基础,所以在介绍C++内存模型之前,作者先简要介绍了一下CPU操作的同步这个概念。

大致翻译:无论在何种指令重排模式(强一致性、弱一致性)下,程序中(programming order)对x变量的write或者后续对x的写或者其他线程对x的写,或者把写改成RMW,在实际的CPU执行顺序中(memory order)对x的读到的值就是刚刚对x的写入的值。

要准确理解这个,首先需要看synchronizes-with以及happens-before的定义。对于一般的原子操作,如果他们直接没有任何上述关系,那么优化器可以方便地把该变量缓存到诸如寄存器什么的地方,只要保证读取和写入的原子性(从上层调用者看来)即可。这就是relaxed的语义。

如果要建立synchronizes-with关系,必须确保对该原子变量的写操作对于所有建立有synchronizes-with关系的线程都可见。简单粗暴的做法是强制的让所有的线程(无论有无synchronizes-with关系)都可见,这就是“序列一致”语义。

序列一致简单是简单,但是要求太严格。因为我们只想在相关的线程上保持序列一致即可。要满足这个条件,一个简单的办法是让相关的线程给这个原子变量打标记,告诉编译器说,注意,针对这个变量的写操作,必须保证所有的打了标记的线程的读操作都能立即看到。这就是acquire/release语义。

内存模型还是《Java Concurrency in Practice》讲得比较易懂。

建议读C++ Concurrency in Action第五章。作者是线程库的提议者和实现者。但是这个深度未必能达到你的期望,因为这些非序列一致的内存序都是为专家准备的,一般不会深入讲解。

synchronize-with相关推荐

  1. synchronize

    1.synchronize方法的产生与vcl的局限性有关,因为vcl控件在同一时刻只能被单线程访问,如果多个线程同时访问vcl,vcl会出现问题.所以问了安全地访问vcl,Tthread类提供了一个方 ...

  2. jps could not synchronize with target

    在启动程序的时候,使用jps查看启动进程时出现:jps could not synchronize with target.程序启动完成之后,jps输出正常.后来在网上查了下,如下: 用jps看had ...

  3. 菜单与工具条的同步 APP_STANDARD.SYNCHRONIZE

    初始情况下,菜单与工具条的状态是一致的,但程序中动态改变某一属性时,工具条并不能相应地改变,所以必须编写代码完成同步. 通过调用以下函数来完成同步: APP_STANDARD.SYNCHRONIZE ...

  4. 多线程:synchronize、volatile、Lock 的区别与用法

    Java多线程之内存可见性和原子性:Synchronized和Volatile的比较 在说明Java多线程内存可见性之前,先来简单了解一下Java内存模型. (1)Java所有变量都存储在主内存中   ...

  5. Synchronize同步

    Volatile 多线程之间的可见性 但是,不具备同步性,也就是原子性 可以算是一个轻量级synchronized 性能比synchronized强很多,不会造成阻塞 在很多开源架构里 比如,nett ...

  6. Synchronize对String加锁解决

    Synchronize 尽量,不要使用String常量加锁 会出现死循环问题 new String 可以使用new String加锁 package com.bjsxt.base.sync006;/* ...

  7. Synchronize对String加锁

    Synchronize 尽量,不要使用String常量加锁 会出现死循环问题 package com.bjsxt.base.sync006;/*** synchronized代码块对字符串的锁,注意S ...

  8. Synchronize对象改变

    Synchronize 对象锁改变 当对一个对象加锁时 如果,对象本身发生改变,那么,持有的锁就不同了 如果,对象本身不改变,那么,依然是同步的 package com.bjsxt.base.sync ...

  9. Synchronize死锁

    Synchronize死锁 双方互相持有,对方的锁 导致,程序无法继续执行 package com.bjsxt.base.sync006;/*** 死锁问题,在设计程序时就应该避免双方相互持有对方的锁 ...

  10. Synchronize锁对象

    Synchronize锁对象 可以对任意的Object对象,进行加锁 用法比较灵活 package com.bjsxt.base.sync006;/*** 使用synchronized代码块加锁,比较 ...

最新文章

  1. ASP.NET Razor – C# 变量简介
  2. 看清本质:程序员为什么会写Bug?
  3. 2021年春季学期-信号与系统-第四次作业参考答案-MATLAB实验1
  4. 软件工程个人项目——买书的最低价格
  5. Spring Boot + Thymeleaf 创建web项目
  6. android开发 RecyclerView 瀑布列表布局
  7. 深度学习中反向传播算法简单推导笔记
  8. 问题:get_params() missing 1 required positional argument: 'self' 之解决
  9. csuoj 1351: Tree Counting
  10. 18.外部相机校准——旋转(Rotation),R是什么样子的,绕Z轴旋转的例子,齐次坐标旋转_2
  11. 计算机主机异常经常蓝屏,计算机频繁发生蓝屏怎么解决
  12. UVA11988 Broken Keyboard (a.k.a. Beiju Text)【输入输出+水题】
  13. (一〇二)静态库(.a)的打包
  14. vue 项目中常见的几个问题
  15. c++ 编程规范技巧
  16. 【时间序列分析】02.线性平稳序列
  17. android缓存bilibili,bilibili缓存姬
  18. Clonezilla SE---克隆linux------转载
  19. 为什么计算机桌面图标不见了,为什么电脑桌面上的图标全不见了?
  20. TypeScript 类型声明文件.d.ts

热门文章

  1. Linux 设置欢迎语
  2. 抖音怎么赚钱,怎样运营好抖音平台
  3. ansible tower 安装方法
  4. 梅科尔工作室-赵凌志-鸿蒙笔记2
  5. C语言--gets()、gets_s()、fget()的比较
  6. 1.安装Kali Linux
  7. 小辣椒手机权限开启方法
  8. WIN7怎么把暴风影视库去除?
  9. python利用百度云接口实现文字OCR功能
  10. 游戏设计的艺术:一本透镜的书——第十二章 游戏机制支撑谜题