以下Java代码看起来有点奇怪,因为我已将其简化为基本要素.我认为代码有一个排序问题.我正在查看JSR-133 Cookbook中的第一个表,看起来普通商店可以使用volatile存储库进行重新排序change().
可以分配给m_normal中change()移动未来的分配m_volatile?换句话说,可以get()回归null吗?
解决这个问题的最佳方法是什么?
private Object m_normal = new Object();
private volatile Object m_volatile;
public void change() {
Object normal;
normal = m_normal; // Must capture value to avoid double-read
if (normal == null) {
return;
}
m_volatile = normal;
m_normal = null;
}
public Object get() {
Object normal;
normal = m_normal; // Must capture value to avoid double-read
if (normal != null) {
return …Run Code Online (Sandbox Code Playgroud) 我正在尝试C++ 0x支持,并且存在一个问题,我想不应该存在.要么我不明白这个主题,要么gcc有一个bug.
我有以下代码,最初x并且y是相同的.线程1始终x先递增,然后递增y.两者都是原子整数值,因此根本没有增量问题.线程2正在检查是否x小于y,如果是,则显示错误消息.
这段代码有时会失败,但为什么呢?这里的问题可能是内存重新排序,但默认情况下所有原子操作都是顺序一致的,我没有明确放松那些操作.我正在x86上编译这段代码,据我所知,该代码不应该有任何订购问题.你能解释一下问题是什么吗?
#include <iostream>
#include <atomic>
#include <thread>
std::atomic_int x;
std::atomic_int y;
void f1()
{
while (true)
{
++x;
++y;
}
}
void f2()
{
while (true)
{
if (x < y)
{
std::cout << "error" << std::endl;
}
}
}
int main()
{
x = 0;
y = 0;
std::thread t1(f1);
std::thread t2(f2);
t1.join();
t2.join();
}
Run Code Online (Sandbox Code Playgroud)
结果可以在这里查看.
我在多个进程之间有一个共享内存,以某种方式插入内存.例如:
DataBlock {
int counter;
double value1;
double ... }
Run Code Online (Sandbox Code Playgroud)
我想要的是计数器以原子方式更新/递增.并且在该地址上发生内存释放.例如,如果我不使用共享内存,那就像是
std::atomic<int> counter;
atomic_store(counter, newvalue, std::memory_order_release); // perform release operation on the affected memory location making the write visible to other threads
Run Code Online (Sandbox Code Playgroud)
如何为随机存储器位置(解释为DataBlock计数器>上面)实现此目的.我可以保证地址符合架构的要求(x86 linux)
- 使更新成为原子 - 如何?(即atomicupdate(addr,newvalue))
- 多核的内存同步 - (即memorysync(addr)) - 我唯一能看到的方法是使用std :: atomic_thread_fence(std :: memory_order_release) - 但这将"建立所有原子和放松原子存储的内存同步排序" - 那就是对我来说太过分了 - 我只想让计数器位置同步.欣赏任何想法.
x86_64上的Linux glibc pthread函数是否作为弱有序内存访问的范围?(pthread_mutex_lock/unlock是我感兴趣的确切函数).
SSE2提供了一些具有弱内存排序的指令(特别是非临时存储,例如movntps).如果您正在使用这些指令并希望保证另一个线程/核心看到一个排序,那么我理解您需要一个明确的栅栏,例如,一个sfence指令.
通常,您确实希望pthread API适当地充当栅栏.但是,我怀疑x86上的正常C代码不会产生弱有序的内存访问,所以我不相信pthreads需要充当弱有序访问的栅栏.
通过glibc pthread源代码读取,最后使用"lock cmpxchgl"实现互斥,至少在无争用路径上.所以我猜我需要知道的是,该指令是否为SSE2弱有序访问的栅栏?
我有一套xchg基于测试的装配锁.我的问题是:
使用指令时是否需要使用内存防护(mfence,sfence或lfence)xchg?
编辑:
64位平台:采用Intel nehalem
我在考虑原子变量是否有可能在获取 - 释放对中加载旧值.假设我们有原子变量x,我们用释放语义存储该变量,然后用获取语义加载它理论上是否可以读取旧值?
std::atomic<int> x = 0;
void thread_1()
{
x.store(1, std::memory_order_release);
}
void thread_2()
{
assert(x.load(std::memory_order_acquire) != 0);
}
Run Code Online (Sandbox Code Playgroud)
如果在线程2加载x(因此存储新值)时功能线程1完成,则线程2是否可以从x加载旧值?换句话说,如果在加载之前完成了对x的实际存储,那么断言可能会触发吗?
据我所知,互联网上的文章是可能的,但我无法理解为什么.存储器生成的内存栅栏到x保证为空存储缓冲区,而从x中加载内存栅栏则保证缓存行无效,因此必须读取最新值.
添加
是否意味着获取 - 释放本身没有任何强制排序?它只是在发布之前发生的任何事情都会在发布之前完成,并且在获取之后所做的一切都将在它之后发生,因此获取 - 释放对强制执行其他操作的排序(为什么??).我做对了吗?这是否意味着在代码中,断言保证不会触发
std::atomic<int> x = 0;
std::atomic<int> y = 0;
void thread_1()
{
y.store(1, std::memory_order_relaxed);
x.store(1, std::memory_order_release);
}
void thread_2()
{
x.load(std::memory_order_acquire);
assert(y.load(std::memory_order_relaxed) != 0);
}
Run Code Online (Sandbox Code Playgroud)
当然,如果线程1已经完成了商店.如果我们用while(x.load()== 0)替换x.load,这将100%工作,但我不知道是什么导致它工作.
如果我用下面的代码替换代码怎么办?
std::atomic<int> x = 0;
void thread_1()
{
x.exchange(1, std::memory_order_acq_rel);
}
void thread_2()
{
assert(x.exchange(0, std::memory_order_acq_rel) != 0);
}
Run Code Online (Sandbox Code Playgroud)
它有什么改变吗?
谢谢.
根据英特尔64和IA-32架构软件开发人员手册,LOCK信号前缀"确保处理器在声明信号时独占使用任何共享内存".这可以是总线或缓存锁的形式.
但是 - 这就是我问这个问题的原因 - 我不清楚,如果这个前缀也提供了任何内存屏障.
我正在多处理器环境中使用NASM进行开发,并且需要使用可选的获取和/或发布语义来实现原子操作.
那么,我是否需要使用MFENCE,SFENCE和LFENCE指令或者这是多余的?
关于这个问题,我只对x86和x86-64感兴趣.
对于MSVC 2005,__ faststorefence的文档说:"保证每个先前的商店在任何后续商店之前全局可见."
对于MSVC 2008和2010,它改为:"保证每个先前的内存引用,包括加载和存储内存引用,在任何后续内存引用之前全局可见."
写入后者的方式,我认为这也会阻止CPU在旧商店之前重新排序负载.这与第一个定义不同,后者暗示内在函数仅用于处理使用旧存储的非临时存储的阻塞或重新排序(唯一的其他重新排序x86(-64)确实如此).
然而,文档似乎与自己相矛盾:"在x64平台上,此例程生成的指令比sfence指令更快的存储范围.在x64平台上使用此内在而不是_mm_sfence."
这意味着它仍然具有类似sfence的功能,因此仍然可以使用较旧的存储重新排序负载.那是哪个呢?有人能解决我的困惑吗?
PS:寻找这个函数的GCC版本,我遇到了long local; __asm__ __volatile__("lock; orl $0, %0;" : : "m"(local));但我觉得它来自32位代码; 什么是64位模拟?
编译器或处理器可以重新排序以下指令,以便另一个线程看到a == 0和b == 1?
假设int a = 0, b = 0;某个地方.
System.Threading.Interlocked.CompareExchange<int>(ref a, 1, 0);
System.Threading.Interlocked.CompareExchange<int>(ref b, 1, 0);
Run Code Online (Sandbox Code Playgroud) 根据OpenMP规范(v4.0),由于不同步的读/写,以下程序包含可能的数据争用i:
int i{0}; // std::atomic<int> i{0};
void write() {
// #pragma omp atomic write // seq_cst
i = 1;
}
int read() {
int j;
// #pragma omp atomic read // seq_cst
j = i;
return j;
}
int main() {
#pragma omp parallel
{ /* code that calls both write() and read() */ }
}
Run Code Online (Sandbox Code Playgroud)
我想到的可能解决方案在代码中显示为注释:
i带#pragma omp atomic write/read,i带#pragma omp atomic write/read seq_cst,std::atomic<int>而不是int …