我正在使用linux,我有两个在另一个线程中读/写的变量.偶尔(100ms),ThreadB读取变量的状态并执行某些操作.它基本上是一个while(1){ dosomething(); usleep(); }.我担心变量会被缓存而且永远不会更新.
什么是确保循环在优化后才能工作的最佳方法?我在想volatile应该做的工作,但我听说它有时不起作用.两个循环都不经常运行(10ms +).访问它们的最简单直接的方法是什么?我正在使用C++ 11
我有点不确定如何使用std::atomic<int>.我可以像普通的int变量一样使用它,它会按预期工作吗?
我是多线程编程的新手,我std::atomic在C++ 11中找到了它.
所以,我试图弄清楚原子操作需要多长时间.
我试过这段代码:
using namespace std;
using namespace std::chrono;
constexpr int NUM_THREADS = 8;
constexpr int LIMIT = 100000;
atomic<int> sum = 0;
void foo(int idx) {
while (true) {
if (sum.load() >= LIMIT) {
return;
}
sum.fetch_add(1);
}
}
Run Code Online (Sandbox Code Playgroud)
用main:
int main(void) {
thread threads[NUM_THREADS];
auto start = high_resolution_clock::now();
for (int i = 0; i < NUM_THREADS; i++) {
threads[i] = thread(&foo, i);
}
for (int i = 0; i < NUM_THREADS; i++) { …Run Code Online (Sandbox Code Playgroud) C++ 标准说,原子上的 RMW(读-修改-写)操作将对原子变量的最新值进行操作。因此memory_order_relaxed,当从多个线程并发执行时,使用这些操作不会影响 RMW 操作。
我假设只有当 RMW 操作存在一些内存屏障或栅栏时,即使指定的内存顺序是“放松的”,这种行为也是可能的。如果我的理解有误,请纠正我,并解释如果不使用此类内存屏障,这些操作如何处理最新值。如果我的理解是正确的,那么我是否可以进一步假设使用 Acquire-Release 或 Seq-CST 内存顺序不应该对 RMW 操作产生额外的性能影响,比如 ARM 或 Alpha 等弱排序架构。提前致谢。
为什么是std::atomic的 store:
std::atomic<int> my_atomic;
my_atomic.store(1, std::memory_order_seq_cst);
Run Code Online (Sandbox Code Playgroud)
在xchg请求具有顺序一致性的商店时执行?
从技术上讲,具有读/写内存屏障的普通商店不应该足够吗?相当于:
_ReadWriteBarrier(); // Or `asm volatile("" ::: "memory");` for gcc/clang
my_atomic.store(1, std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
我明确地谈论x86和x86_64.商店有隐含的获取围栏.
给定一个共享的整数计数器数组,我想知道线程是否可以在不锁定整个数组的情况下自动获取和添加数组元素?
这是使用互斥锁锁定对整个数组的访问的工作模型的说明。
// thread-shared class members
std::mutex count_array_mutex_;
std::vector<int> counter_array_( 100ish );
// Thread critical section
int counter_index = ... // unpredictable index
int current_count;
{
std::lock_guard<std::mutex> lock(count_array_mutex_);
current_count = counter_array_[counter_index] ++;
}
// ... do stuff using current_count.
Run Code Online (Sandbox Code Playgroud)
我希望多个线程能够同时获取添加单独的数组元素。
到目前为止,在我对std::atomic<int>I 的研究中,构造原子对象也构造了受保护的成员。(还有很多答案解释了为什么你不能做一个std::vector<std::atomic<int> >)
我看到g ++生成一个简单的movfor x.load()和mov+ mfencefor x.store(y).考虑这个经典的例子:
#include<atomic>
#include<thread>
std::atomic<bool> x,y;
bool r1;
bool r2;
void go1(){
x.store(true);
}
void go2(){
y.store(true);
}
bool go3(){
bool a=x.load();
bool b=y.load();
r1 = a && !b;
}
bool go4(){
bool b=y.load();
bool a=x.load();
r2= b && !a;
}
int main() {
std::thread t1(go1);
std::thread t2(go2);
std::thread t3(go3);
std::thread t4(go4);
t1.join();
t2.join();
t3.join();
t4.join();
return r1*2 + r2;
}
Run Code Online (Sandbox Code Playgroud)
其中根据https://godbolt.org/z/APS4ZY go1和go2被翻译成
go1():
mov BYTE PTR x[rip], 1 …Run Code Online (Sandbox Code Playgroud) 我读过有关携带依赖关系和依赖关系排序之前,在其定义中使用一个5.1.2.4(p16):
在以下情况下,评估
A在评估之前是依赖顺序的B:—
A对原子对象执行释放操作M,并在另一个线程中B执行消耗操作M并读取以 为首的释放序列中的任何副作用写入的值A,或— 对于某些求值
X,A之前是依存顺序X并X带有对 的依存关系B。
所以我试图制作一个可能有用的例子。就这个:
static _Atomic int i;
void *produce(void *ptr){
int int_value = *((int *) ptr);
atomic_store_explicit(&i, int_value, memory_order_release);
return NULL;
}
void *consume(void *ignored){
int int_value = atomic_load_explicit(&i, memory_order_consume);
int new_int_value = int_value + 42;
printf("Consumed = %d\n", new_int_value);
}
int main(int …Run Code Online (Sandbox Code Playgroud) 我正在研究C++11 中mutex和之间的区别atomic。
据我了解,mutex是一种基于操作系统/内核实现的锁机制。例如,Linux 提供了一种机制,即futex. 在 的帮助下futex,我们可以实现mutex和semaphore。此外,我知道这futex是由低级原子操作实现的,例如CompareAndSet, CompareAndSwap。
对于std::atomic,我知道它是基于C++11引入的内存模型实现的。但是,我不知道内存模型在低级别是如何实现的。如果也是通过原子操作之类CompareAndSet的方式实现的,std::atomic和 和 有mutex什么区别?
总之,如果std::atomic::is_lock_free给我一个false,好吧,我会说这std::atomic与mutex. 但是如果它给了我一个true,它是如何在低级别实现的?
在问了这个问题之后,我了解到原子指令,例如test-and-set,不会涉及内核。只有当进程需要进入睡眠状态(等待获取锁)或唤醒(因为它无法获取锁但现在可以)时,内核才必须参与执行调度操作。
如果是这样,是否意味着内存栅栏(例如std::atomic_thread_fence在 c++11 中)不会也涉及内核?
考虑以下代码,它使用 astd::atomic原子地加载 64 位对象。
#include <atomic>
struct A {
int32_t x, y;
};
A f(std::atomic<A>& a) {
return a.load(std::memory_order_relaxed);
}
Run Code Online (Sandbox Code Playgroud)
使用 GCC,好事发生了,并生成了以下代码。( https://godbolt.org/z/zS53ZF )
f(std::atomic<A>&):
mov rax, QWORD PTR [rdi]
ret
Run Code Online (Sandbox Code Playgroud)
这正是我所期望的,因为我看不出为什么在这种情况下 64 位结构不能像任何其他 64 位字一样被对待。
但是,对于 Clang,情况就不同了。Clang 生成以下内容。( https://godbolt.org/z/d6uqrP )
f(std::atomic<A>&): # @f(std::atomic<A>&)
push rax
mov rsi, rdi
mov rdx, rsp
mov edi, 8
xor ecx, ecx
call __atomic_load
mov rax, qword ptr [rsp]
pop rcx
ret
mov rdi, rax
call __clang_call_terminate
__clang_call_terminate: # @__clang_call_terminate
push rax …Run Code Online (Sandbox Code Playgroud)