以原子方式将一个或多个位复制到整数中

Bru*_*ong 3 c++ bit-manipulation atomic bit

我有一些代码,通过首先清除目标 int 中的掩码位,然后将它们或运算到 int 中,将掩码位复制到整数中。

像这样:

bitsToSet = 6
targetInt &= ~(1 << bitsToSet)
targetInt |= desiredBitValue << bitsToSet
Run Code Online (Sandbox Code Playgroud)

问题是它现在需要是线程安全的,并且我需要使操作原子化。我认为使用 std:atomic<int> 只会使每个子操作成为原子操作,而不是整个操作。

如何使整个操作(包括 &= 和 |= 操作)原子化?

例如,如果我有一个函数(或者更好的是宏),SetBits(TARGET, MASK, VALUE)它可以自动将 TARGET 中的 MASKed 位设置为 VALUE,那么它就会为我解决问题。MASK 和 VALUE 已经可以左移。

我当前的非原子代码是

#define SetBits(TARGET, MASK, VALUE) {(TARGET) &= ~((uint64_t)MASK); (TARGET)|=((uint64_t)VALUE);}
Run Code Online (Sandbox Code Playgroud)

Mil*_*nek 5

您可以使用比较交换循环:

void SetBitsAtomic(std::atomic<int>& target, int mask, int value) {
    int original_value = target.load();
    int new_value = original_value;
    SetBits(new_value, mask, value);
    while (!target.compare_exchange_weak(original_value, new_value)) {
        // Another thread may have changed target between when we read
        // it and when we tried to write to it, so try again with the
        // updated value
        new_value = original_value;
        SetBits(new_value, mask, value);
    }
}
Run Code Online (Sandbox Code Playgroud)

这将读取 的原始值target,执行屏蔽操作,然后target仅当自读取以来没有其他线程对其进行修改时,才将修改后的值写回 。如果另一个线程已修改target,则其更新的值将被写入original_value,并且它会继续尝试,直到它设法target先于其他线程进行更新。

请注意,我在这里对loadcompare_exchange_weak操作使用了(默认)完全顺序一致性。您可能不需要完全的顺序一致性,但如果没有更多关于您使用它的用途的信息,我无法确切地知道您需要什么。


或者,您可以只使用互斥体:

std::mutex mtx;

void SetBitsAtomic(int& target, int mask, int value) {
    std::lock_guard lock{mtx};
    SetBits(target, mask, value);
}
Run Code Online (Sandbox Code Playgroud)

可能比无锁compare_exchange_weak版本的性能要差,但这实际上取决于它的用途。它当然更简单、更容易推理,在您的情况下,这可能比原始性能更重要。