C++在两个不同的变量上使用memory_order_relaxed
C++ using memory_order_relaxed on two different variables
在ThreadMethodOne中加载时,放松变量valA和valB同步的最正确方法是什么(假设没有错误的valA和valB缓存线共享)?我似乎不应该将ThreadMethodOne更改为使用memory_order_relaxed来加载valA,因为编译器可以在valB.load之后移动valA.load,因为一旦进行更改,valB.load上的memory_orderAcquire就不能保护valA在valB.load之后移动。我似乎也不能在valB.load上使用memory_order_relaxed,因为它将不再与ThreadMethodTwo中的fetch_add同步。交换物品并放松valA的负荷会更好吗?
这是正确的零钱吗?
nTotal += valB.load(std::memory_order_acquire);
nTotal += valA.load(std::memory_order_relaxed);
在编译器资源管理器上查看结果似乎显示,当对valA或valB使用memory_order_relaxed时,即使我不交换指令的顺序,ThreadMethodOne也会生成相同的代码。我还看到ThreadMethodTwo中的memory_order_released仍然编译为与memory_order_release相同。将memory_order_relaxed更改为以下行似乎使其成为非锁定添加"valA.store(valA.load(std::memory_order_relaxed)+1,std::memory_order_laxed);"但我不知道这样是否更好。
完整程序:
#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include <atomic>
#include <unistd.h>
bool bDone { false };
std::atomic_int valA {0};
std::atomic_int valB {0};
void ThreadMethodOne()
{
while (!bDone)
{
int nTotal {0};
nTotal += valA.load(std::memory_order_acquire);
nTotal += valB.load(std::memory_order_acquire);
printf("Thread total %dn", nTotal);
}
}
void ThreadMethodTwo()
{
while (!bDone)
{
valA.fetch_add(1, std::memory_order_relaxed);
valB.fetch_add(1, std::memory_order_release);
}
}
int main()
{
std::thread tOne(ThreadMethodOne);
std::thread tTwo(ThreadMethodTwo);
usleep(100000);
bDone = true;
tOne.join();
tTwo.join();
int nTotal = valA.load(std::memory_order_acquire);
nTotal += valB.load(std::memory_order_acquire);
printf("Completed total %dn", nTotal);
}
一个更好的样本离开了原来的一个,因为它是在评论中写的一个
#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include <atomic>
#include <unistd.h>
std::atomic_bool bDone { false };
std::atomic_int valA {0};
std::atomic_int valB {0};
void ThreadMethodOne()
{
while (!bDone)
{
int nTotalA = valA.load(std::memory_order_acquire);
int nTotalB = valB.load(std::memory_order_relaxed);
printf("Thread total A: %d B: %dn", nTotalA, nTotalB);
}
}
void ThreadMethodTwo()
{
while (!bDone)
{
valB.fetch_add(1, std::memory_order_relaxed);
valA.fetch_add(1, std::memory_order_release);
}
}
int main()
{
std::thread tOne(ThreadMethodOne);
std::thread tTwo(ThreadMethodTwo);
usleep(100000);
bDone = true;
tOne.join();
tTwo.join();
int nTotalA = valA.load(std::memory_order_acquire);
int nTotalB = valB.load(std::memory_order_relaxed);
printf("Completed total A: %d B: %dn", nTotalA, nTotalB);
}
清理完代码后,请参阅我的评论,我们得到了类似的内容
#include <atomic>
#include <iostream>
std::atomic_int valA {0};
std::atomic_int valB {0};
void ThreadMethodOne()
{
int nTotalA = valA.load(std::memory_order_acquire);
int nTotalB = valB.load(std::memory_order_relaxed);
std::cout << "Thread total A: " << nTotalA << " B: " << nTotalB << 'n';
}
void ThreadMethodTwo()
{
valB.fetch_add(1, std::memory_order_relaxed);
valA.fetch_add(1, std::memory_order_release);
}
int main()
{
std::thread tOne(ThreadMethodOne);
std::thread tTwo(ThreadMethodTwo);
tOne.join();
tTwo.join();
int nTotalA = valA.load(std::memory_order_acquire);
int nTotalB = valB.load(std::memory_order_relaxed);
std::cout << "Completed total A: " << nTotalA << " B: " << nTotalB << 'n';
}
该计划的可能结果是:
Thread total A: 0 B: 0
Completed total A: 1 B: 1
或
Thread total A: 0 B: 1
Completed total A: 1 B: 1
或
Thread total A: 1 B: 1
Completed total A: 1 B: 1
它总是打印Completed total A: 1 B: 1
的原因是线程2被连接并因此完成,这为每个变量添加了1,而线程1中的负载对此没有影响。
如果线程1在线程2之前全部运行并完成,那么它显然将打印0 0,而如果线程2在线程1之前全部运行和完成,那么线程1将打印1 1。请注意,在线程1中执行memory_order_aquire加载不会强制执行任何操作。它可以很容易地读取0的初始值。
如果线程或多或少同时运行,那么0 1的结果也是微不足道的:线程1可能会执行它的第一行,然后线程2执行它的两行,最后线程1读取线程2写入valB的值(这不是必须的,因为它是宽松的,但在这种情况下,我们只得到0 0的输出;但是,如果我们等待足够长的时间,它至少有可能读取1)。
所以,唯一感兴趣的问题是:为什么我们看不到10的输出?
原因是,如果线程1读取valA的值1,那么该值必须是线程2写入的值。这里,其值被读取的写入是写入释放,而读取本身是读取获取。这导致同步发生,导致线程2在写释放之前发生的每一个副作用在读释放之后对线程1中的每一次内存访问都可见。换句话说,如果我们读取valA==1,那么valB的后续读取(放松或不放松)将看到线程2对valB的写入,因此总是看到1,而从不看到0。
不幸的是,我不能对此说更多,因为你的问题非常不清楚:我不知道你期望或想要的结果是什么;所以我不能说要实现这一点的内存需求。
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 将数组的地址分配给变量并删除
- 为"adjacent"变量赋值时出现问题
- enum是C++中的宏变量还是整数变量
- 在全局变量中保存类的实例以重新创建类(创建"backup")
- 用C++中的一个变量定义一个常量
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 你能重载对象变量名本身返回的内容吗
- 内置函数可查看CPP中的成员变量
- 是否可以初始化不可复制类型的成员变量(或基类)
- 尝试通过多个向量访问变量时,向量下标超出范围
- 试图让变量检查数组中的某些内容
- Cpp-Tuple使用带有变量的get
- 将包含C样式数组的对象初始化为成员变量(C++)
- 当vector是tje全局变量时,c++中vector的内存管理
- 通过多个头文件使用常量变量
- std::threads可以从Windows DLL中的全局变量创建/销毁吗?
- 执行函数时导致崩溃的变量
- POSIX 线程 - 使用条件变量 MEMORY LEAK 同步分离的线程