在C/C++中原子地按位与一个字节的最佳方法?

jtoj6r0c  于 8个月前  发布在  C/C++
关注(0)|答案(1)|浏览(111)

目前正在使用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序列,最好的原子方法是什么?

euoag5mw

euoag5mw1#

[我]发现内存中自然对齐的全局变量具有原子读写。
这在C/C意义上是不正确的,只有在x86_64意义上是正确的。确实,x86_64上的任何对齐的加载和存储都是原子的,但这对于抽象机器来说是不正确的。并发写入非原子内存位 * 总是 * 一个数据竞争,线程消毒器可能会捕捉到错误,即使架构理论上是安全的。
此外,以原子方式执行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函数形式的内置方法更糟糕。如果体系结构不直接提供原子取与指令,则应选择最佳方式。你不用担心。

参见

相关问题