这是真的,const_cast会仅仅是一个的方式来告诉编译器"停止呻吟,把它当作一个非const指针"?有没有将const_cast本身转换为实际机器代码的情况?
当且仅当所有顺序一致的执行没有数据争用时,程序才能正确同步.
根据讨论,如果一个正确同步的程序仍允许数据竞争吗?(第一部分),我们得出以下结论:
程序可以正确同步并具有数据竞争.
两个结论的结合意味着它必须存在这样一个例子:
程序的所有顺序一致的执行都是数据竞争的,但是这样的程序的正常执行(除了顺序一致的执行之外的执行)包含数据竞争.
经过深思熟虑,我还是找不到这样的代码样本.那你呢?
我java.lang.Class.newInstance0()在JDK 1.7 Update 7中看到了以下注释:
注意:在当前Java内存模型下,以下代码可能不严格正确.
任何人都可以解释一下原因吗?
根据Java语言规范(例17.4-1),以下片段(从开始A == B == 0)...
Thread 1 Thread 2
-------- --------
r2 = A; r1 = B;
B = 1; A = 2;
Run Code Online (Sandbox Code Playgroud)
......可以导致r2 == 2和r1 == 1.这是因为执行的结果B = 1;不依赖于是否r2 = A已经执行,因此JVM可以自由地交换这两个指令的执行顺序.换句话说,规范允许以下交错:
Thread 1 Thread 2
-------- --------
B = 1;
r1 = B;
A = 2;
r2 = A;
Run Code Online (Sandbox Code Playgroud)
这显然导致r2 == 1和r1 == 1.
假设我们稍微调整一下这个例子:
Thread 1 Thread 2
-------- --------
r2 …Run Code Online (Sandbox Code Playgroud) 在TILE-Gx架构的Linux内核自旋锁实现中,看起来它们在锁定时不会发出任何内存障碍(仅在解锁时):
https://github.com/torvalds/linux/blob/master/arch/tile/include/asm/spinlock_64.h
然后我不明白为什么指令不能在锁定之上重新排序,这会导致程序员认为在保持锁定时执行的指令,在锁定之前实际执行?
其他架构似乎至少有一个编译器障碍:
ARM的自旋锁具有内存屏障:
https://github.com/torvalds/linux/blob/master/arch/arm/include/asm/spinlock.h
评论:
Run Code Online (Sandbox Code Playgroud)A memory barrier is required after we get a lock, and before we release it, because V6 CPUs are assumed to have weakly ordered memory.
x86的spinlock有一个编译器障碍:
https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/spinlock.h
评论:
Run Code Online (Sandbox Code Playgroud)barrier(); /* make sure nothing creeps before the lock is taken */
为什么TILE-Gx不同?我认为它的内存模型和ARM的内存模型一样弱.为什么他们甚至没有编译器障碍?
multithreading memory-model linux-kernel memory-barriers tilera
您认为在Java中实现发布/获取对的获取部分的最佳方法是什么?
我正在尝试使用经典的发布/获取语义模拟我的应用程序中的一些操作(没有StoreLoad和没有跨线程的顺序一致性).
有几种方法可以实现JDK中存储释放的粗略等效.java.util.concurrent.Atomic*.lazySet()并且sun.misc.Unsafe.putOrdered*()最常被引用的方法是做到这一点.然而,没有明显的方法来实现负载获取.
JDK API主要允许在内部lazySet()使用volatile变量,因此它们的存储版本与易失性负载配对.理论上,易失性负载应该比负载获取更昂贵,并且在前面的存储释放的上下文中不应该提供比纯粹的负载获取更多的东西.
sun.misc.Unsafe虽然这些获取方法是针对即将推出的VarHandles API计划的,但并未提供方法的getAcquire()*等效putOrdered*()方法.
听起来像它会起作用的东西是明显的负荷,接着是sun.misc.Unsafe.loadFence().有点令人不安的是,我还没有在其他任何地方看到过这种情况.这可能与它是一个非常丑陋的黑客的事实有关.
PS我很清楚JMM没有涵盖这些机制,它们不足以维持顺序一致性,并且它们创建的动作不是同步动作(例如我理解它们例如打破了IRIW).我也理解,提供的商店版本Atomic*/Unsafe通常用于急切地将引用或生产者/消费者场景中的空白作为一些重要索引的优化消息传递机制.
java concurrency multithreading memory-model memory-barriers
比如说,我Foo在线程#1中创建了一个类型的对象,并希望能够在线程#3中访问它.
我可以尝试这样的事情:
std::atomic<int> sync{10};
Foo *fp;
// thread 1: modifies sync: 10 -> 11
fp = new Foo;
sync.store(11, std::memory_order_release);
// thread 2a: modifies sync: 11 -> 12
while (sync.load(std::memory_order_relaxed) != 11);
sync.store(12, std::memory_order_relaxed);
// thread 3
while (sync.load(std::memory_order_acquire) != 12);
fp->do_something();
Run Code Online (Sandbox Code Playgroud)
Foo,更新为11sync增加到12该场景被破坏,因为线程#3旋转直到它加载12,其可能无序到达(wrt 11)并且Foo不与12一起订购(由于线程#2a中的放松操作).
这有点违反直觉,因为修改顺序sync是10→11→12
标准说(§1.10.1-6):
原子存储 - 释放与从存储中获取其值的load-acquire同步(29.3).[注意:除了在指定的情况下,读取更高的值不一定能确保可见性,如下所述.这样的要求有时会干扰有效的实施. - 尾注]
它也在(§1.10.1-5)中说:
由原子对象M上的释放操作A引导的释放序列是M的修改顺序中的副作用的最大连续子序列,其中第一操作是A,并且每个后续操作
- 由执行A的相同线程执行. ,或
- 是原子读 - 修改 - 写操作.
现在,线程#2a被修改为使用原子读 - 修改 …
在C ++草案标准中多次使用了“在……之前发生”一词。
例如:终止 [basic.start.term] / 5
如果在调用std?::?atexit之前强烈完成了具有静态存储持续时间的对象的初始化(请参见[support.start.term]),则对该函数的调用将传递给std?::?atexit。在调用对象的析构函数之前进行排序。如果对std?::?atexit的调用很强地发生在具有静态存储持续时间的对象的初始化完成之前,则在传递给std?::?atexit的函数的调用之前对对象的析构函数的调用进行排序。 。如果对std?::?atexit的调用在另一次对std?::?atexit的调用之前强烈发生,则传递给第二个std?::?atexit调用的函数的调用将在传递给函数的函数的调用之前按顺序进行。第一个std?::?atexit调用。
并在 数据竞赛 [intro.races] / 12中定义
评估A发生在评估D之前,如果发生以下情况之一
(12.1)A在D之前排序,或
(12.2)A与D同步,并且A和D都是顺序一致的原子操作([atomics.order]),或者
(12.3)对B和C进行求值,使得A在B之前排序,B恰好在C之前发生,而C在D之前排序,或者
(12.4)有一个评估B,使得A强烈发生在B之前,而B强烈发生在D之前。
[注意:非正式地,如果A强烈地发生在B之前,那么在所有情况下A似乎都在B之前被评估。强烈发生在排除消耗操作之前。—尾注]
为什么引入“强烈发生”?直觉上,它与“之前发生的事情”有什么区别和关系?
注释中的“在所有情况下A似乎都在B之前被评估”是什么意思?
(注意:此问题的动机是Peter Cordes在此答案下的评论。)
标准报价附加草案(感谢Peter Cordes)
满足以下约束的所有memory_order?::?seq_cst操作(包括栅栏)上只有一个总顺序S。首先,如果A和B是memory_order?::?seq_cst运算,并且A强烈地发生在B之前,那么A在S中先于B。其次,对于对象M上的每对原子操作A和B,A的相干性在B之前,S必须满足以下四个条件:
(4.1)如果A和B都是memory_order?::?seq_cst操作,则A在S中先于B;和
(4.2)如果A是一个memory_order?::?seq_cst操作,而B发生在memory_order?::?seq_cst栅栏Y之前,则A在S中位于Y之前;和
(4.3)如果memory_order?::?seq_cst栅栏X发生在A之前,而B是memory_order?::?seq_cst操作,则X在S之前位于B之前;和
(4.4)如果memory_order?::?seq_cst防护栏X发生在A之前,而B发生在memory_order?::?seq_cst防护栏Y之前,则X在S中位于Y之前。
c++ multithreading memory-model language-lawyer happens-before
想象一下运行以下简单代码的 N 个线程:
int res = num.fetch_add(1, std::memory_order_relaxed);
Run Code Online (Sandbox Code Playgroud)
在哪里num:
std::atomic<int> num = 0;
Run Code Online (Sandbox Code Playgroud)
假设res对于每个运行代码的线程来说是否完全安全,或者对于某些线程来说它可能是相同的?
ARMv8.3 引入了新指令:LDAPR。
当 STLR 后跟 LDAR 到不同的地址时,这两个不能重新排序,因此称为 RCsc(释放一致顺序一致)。
当 STLR 后跟 LDAPR 到不同的地址时,这 2 个地址可以重新排序。这称为RCpc(发布一致处理器一致)。
我的问题是PC部分。
PC 是 TSO 的松弛,其中 TSO 是多副本原子,而 PC 是非多副本原子。
ARMv8的内存模型已改进为多副本原子,因为没有供应商创建过非多副本原子微体系结构,这使得内存模型更加复杂。
所以我遇到了矛盾。
关键问题是:每个存储(包括宽松的存储)都是多副本原子的吗?
如果是这样,那么 rcpc 的 PC 部分对我来说没有意义,因为 PC 是非多副本原子的。由于 ARM 过去是非多副本原子的,它是否可能是一个遗留名称?
PC有多种定义;所以也许这就是原因。
memory-model ×10
c++ ×4
java ×4
atomic ×2
concurrency ×2
jls ×2
arm ×1
armv8 ×1
c++11 ×1
const ×1
const-cast ×1
jvm ×1
linux-kernel ×1
tilera ×1