目前正在使用GCC研究C/C++中的原子操作,发现内存中自然对齐的全局变量具有原子读写。
然而,我试图按位与一个全局变量,并注意到它归结为一个读-修改-写序列,如果有多个线程操作该字节值,这是很麻烦的。
经过一番研究,我选择了这两个例子:
C Example- GCC扩展__sync_fetch_and_and
#include <stdio.h>
#include <stdint.h>
uint8_t byteC = 0xFF;
int main() {
__sync_fetch_and_and(&byteC, 0xF0);
printf("Value of byteC: 0x%X\n", byteC);
return 0;
}
C++ Example- C++11使用原子fetch_and
#include <iostream>
#include <atomic>
std::atomic<uint8_t> byteCpp(0xFF);
int main() {
byteCpp.fetch_and(0xF0);
std::cout << "Value of byteCpp: 0x" << std::hex << static_cast<int>(byteCpp.load()) << std::endl;
return 0;
}
其他例子如下,但它们似乎不那么直观,计算成本更高。
使用pthread_mutex_lock
uint8_t byte = 0xFF;
pthread_mutex_t byte_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&byte_mutex);
byte &= 0xF0;
pthread_mutex_unlock(&byte_mutex);
使用互斥量lock_guard
#include <mutex>
uint8_t byte;
std::mutex byte_mutex;
void atomic_and() {
std::lock_guard<std::mutex> lock(byte_mutex);
byte &= 0xF0;
}
使用compare_exchange_weak
std::atomic<uint8_t> byte;
void atomic_and() {
uint8_t old_val, new_val;
do {
old_val = byte.load();
new_val = old_val & 0xF0;
} while (!byte.compare_exchange_weak(old_val, new_val));
}
问题
对于多线程C/C++程序中的read-modify-write序列,最好的原子方法是什么?
1条答案
按热度按时间euoag5mw1#
[我]发现内存中自然对齐的全局变量具有原子读写。
这在C/C意义上是不正确的,只有在x86_64意义上是正确的。确实,x86_64上的任何对齐的加载和存储都是原子的,但这对于抽象机器来说是不正确的。并发写入非原子内存位 * 总是 * 一个数据竞争,线程消毒器可能会捕捉到错误,即使架构理论上是安全的。
此外,以原子方式执行
byte &= 0xf0
的最佳方法在C和C中非常相似:其他方法(POSIX线程,
std::mutex
,compare_exchange
重试循环)几乎肯定比fetch_and
函数形式的内置方法更糟糕。如果体系结构不直接提供原子取与指令,则应选择最佳方式。你不用担心。参见