synchronize-with
只有真正理解这个概念,才能理解多线程的核心。单个线程的重排可以通过原子操作搞定,但多个线程之间的原子操作没有内在的关系,相当于是两个自治的操作序列,或者说两个局部时间域。如何将这两个时间的某些操作强行规定一个先后关系,而其他操作不管,就是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相关推荐
- synchronize
1.synchronize方法的产生与vcl的局限性有关,因为vcl控件在同一时刻只能被单线程访问,如果多个线程同时访问vcl,vcl会出现问题.所以问了安全地访问vcl,Tthread类提供了一个方 ...
- jps could not synchronize with target
在启动程序的时候,使用jps查看启动进程时出现:jps could not synchronize with target.程序启动完成之后,jps输出正常.后来在网上查了下,如下: 用jps看had ...
- 菜单与工具条的同步 APP_STANDARD.SYNCHRONIZE
初始情况下,菜单与工具条的状态是一致的,但程序中动态改变某一属性时,工具条并不能相应地改变,所以必须编写代码完成同步. 通过调用以下函数来完成同步: APP_STANDARD.SYNCHRONIZE ...
- 多线程:synchronize、volatile、Lock 的区别与用法
Java多线程之内存可见性和原子性:Synchronized和Volatile的比较 在说明Java多线程内存可见性之前,先来简单了解一下Java内存模型. (1)Java所有变量都存储在主内存中 ...
- Synchronize同步
Volatile 多线程之间的可见性 但是,不具备同步性,也就是原子性 可以算是一个轻量级synchronized 性能比synchronized强很多,不会造成阻塞 在很多开源架构里 比如,nett ...
- Synchronize对String加锁解决
Synchronize 尽量,不要使用String常量加锁 会出现死循环问题 new String 可以使用new String加锁 package com.bjsxt.base.sync006;/* ...
- Synchronize对String加锁
Synchronize 尽量,不要使用String常量加锁 会出现死循环问题 package com.bjsxt.base.sync006;/*** synchronized代码块对字符串的锁,注意S ...
- Synchronize对象改变
Synchronize 对象锁改变 当对一个对象加锁时 如果,对象本身发生改变,那么,持有的锁就不同了 如果,对象本身不改变,那么,依然是同步的 package com.bjsxt.base.sync ...
- Synchronize死锁
Synchronize死锁 双方互相持有,对方的锁 导致,程序无法继续执行 package com.bjsxt.base.sync006;/*** 死锁问题,在设计程序时就应该避免双方相互持有对方的锁 ...
- Synchronize锁对象
Synchronize锁对象 可以对任意的Object对象,进行加锁 用法比较灵活 package com.bjsxt.base.sync006;/*** 使用synchronized代码块加锁,比较 ...
最新文章
- ASP.NET Razor – C# 变量简介
- 看清本质:程序员为什么会写Bug?
- 2021年春季学期-信号与系统-第四次作业参考答案-MATLAB实验1
- 软件工程个人项目——买书的最低价格
- Spring Boot + Thymeleaf 创建web项目
- android开发 RecyclerView 瀑布列表布局
- 深度学习中反向传播算法简单推导笔记
- 问题:get_params() missing 1 required positional argument: 'self' 之解决
- csuoj 1351: Tree Counting
- 18.外部相机校准——旋转(Rotation),R是什么样子的,绕Z轴旋转的例子,齐次坐标旋转_2
- 计算机主机异常经常蓝屏,计算机频繁发生蓝屏怎么解决
- UVA11988 Broken Keyboard (a.k.a. Beiju Text)【输入输出+水题】
- (一〇二)静态库(.a)的打包
- vue 项目中常见的几个问题
- c++ 编程规范技巧
- 【时间序列分析】02.线性平稳序列
- android缓存bilibili,bilibili缓存姬
- Clonezilla SE---克隆linux------转载
- 为什么计算机桌面图标不见了,为什么电脑桌面上的图标全不见了?
- TypeScript 类型声明文件.d.ts