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

问题描述 投票:0回答:1

目前正在使用 GCC 查看 C/C++ 中的原子操作,发现内存中自然对齐的全局变量具有原子读取和写入。

但是,我试图对全局变量进行按位与运算,并注意到它归结为一个“读取-修改-写入”序列,如果有多个线程对该字节值进行操作,那么这会很麻烦。 经过一番研究,我选择了这两个例子:

C 示例

- 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++ 示例

- 使用原子的 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++ 程序中

读取-修改-写入

序列的最佳原子方法是什么?

c++ c atomic
1个回答
2
投票
[我]发现内存中自然对齐的全局变量具有原子读取和写入。

这在 C/C++ 意义上是不正确的,仅在 x86_64 意义上是正确的。确实,x86_64 上的任何对齐加载和存储都是原子的,但这对于抽象机来说是不正确的。同时写入非原子内存位“总是”是一场数据竞争,线程清理程序可能会发现错误,即使架构理论上使其安全。

此外,最好的方法在 C 和 C++ 中非常相似: // desired operation byte &= 0xF0; // C++, assuming 'byte' is std::atomic_uint8_t std::uint8_t old = byte.fetch_and(0xf0); /* optionally specify memory order */ // C, assuming 'byte' is atomic_uint8_t // (from <stdatomic.h>, no compiler extensions needed) uint8_t old = atomic_fetch_and(&byte, 0xf0); /* optionally atomic_fetch_and_explicit */

其他方法(POSIX 线程、

std::mutex
compare_exchange

重试循环)几乎肯定比

fetch_and
函数形式的内置方法更糟糕。
    

© www.soinside.com 2019 - 2024. All rights reserved.