目前用GCC研究了C/C++中的原子操作,发现内存中自然对齐的全局变量都有原子读写.

然而,我试图按位AND全局变量,并注意到它归结为一个read-modify-write序列,如果有多个线程对该字节值进行操作,这是很麻烦的.

经过一些研究,我 Select 了这两个例子:

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;
}

使用Atom fetch_andC++ Example-C++11

#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));
}

Question

对于多线程C/C++程序中的read-modify-write序列,最好的原子方法是什么?

推荐答案

[我已经]发现,内存中自然对齐的全局变量具有原子读写.

这在C/C++意义上是不正确的,仅在x86_64意义上是不正确的.确实,x86_64上的任何对齐加载和存储都是原子的,但这对于抽象机器是不正确的.并发写入非原子内存位是always次数据竞争,线程消毒器可能会捕获错误,即使理论上架构使其安全.

此外,原子计算byte &= 0xf0的最佳方法在C和C++中非常相似:

// C++
#include <atomic>
std::atomic_uint8_t byte; // or std::atomic<std::uint8_t>
// ...
std::uint8_t old = byte.fetch_and(0xf0); /* optionally specify memory order */
// or
std::uint8_t old = std::atomic_fetch_and(&byte, 0xf0);
// C (no compiler extensions/intrinsics needed)
#include <stdatomic.h>
atomic_uint8_t byte; // or _Atomic uint8_t
// ...
uint8_t old = atomic_fetch_and(&byte, 0xf0); /* optionally atomic_fetch_and_explicit */

其他方法(POSIX线程、std::mutexcompare_exchange重试循环)几乎肯定比fetch_and个函数形式的内置方法差.如果体系 struct 不直接提供原子FETCH-AND指令,那么应该 Select 哪种方式最好.这不是你必须担心的事情.


看见

Thanks to @PeterCordes for sharing these links.

C++相关问答推荐

Pure Win32 C(++)-除了替换控件的窗口程序之外,还有其他方法可以在输入时禁用按钮吗?

C strlen on char array

如何避免重新分配指针数组时,我们从一开始就不知道确切的大小

通过MQTT/蚊子发送大文件—限制在4MB

ATmega328P EEPROM未写入

如何捕捉只有换行符或空格字符缓冲区的边缘大小写

在为hashmap创建加载器时,我的存储桶指向它自己

如何在CANbus RX/TX FIFO起始地址寄存器(ATSAME 51)的特定地址初始化数组?

CC2538裸机项目编译但不起作用

防止规范模式在C++中 echo 特殊字符

如何使用FSeek和文件流指针在C中查找文件的前一个元素和前一个减go 一个元素

通过k&;r语法的c声明无效

为什么我的二叉树删除删除整个左部分的树?

传递给函数的 struct 中的数组

C程序printf在getchar while循环后不工作

通过char*访问指针的对象表示是未定义的行为吗?

struct 中的qsort,但排序后的 struct 很乱

C/C++编译器可以在编译过程中通过按引用传递来优化按值传递吗?

C23 中是否有 __attribute__((nonnull)) 的等效项?

c 函数指针,另一种语法