std::atomic on struct bit-fields



我正在修改一些现有的开源库,并且有一个包含位字段的结构(例如名为 Node(,例如

struct Node {
    std::atomic<uint32_t> size:30;
    std::atomic<uint32_t> isnull:1;
};

为了满足我的需求,这些字段必须是原子的,所以我期望为此使用 std::atomic 并面临编译时错误:

bit-field 'size' has non-integral type 'std::atomic<uint32_t>'

根据文档,有一组有限的类型可用于 std::atomic

任何人都可以建议/有想法,如何在对现有源代码的影响最小的情况下获得原子字段的功能?

提前感谢!

我在下面使用了一个无符号的短裤作为示例。

这不太理想,但您可以牺牲 8 位并在带有联合的位字段中插入一个std::atomic_flag。不幸的是,std::atomic_flag类型是std::atomic_bool类型。

每次访问此结构时,都可以手动旋转锁定。但是,代码的性能下降应该最小(与创建、锁定、解锁、使用 std::mutexstd::unique_lock 销毁不同(。

此代码可能会浪费大约 10-30 个时钟周期来实现低成本多线程。

确保下面保留的 8 位没有被处理器的字节序结构弄乱。您可能必须在最后定义大端处理器。我只在英特尔 CPU(总是小端序(上测试了这段代码。

#include <iostream>
#include <atomic>
#include <thread>
union Data
{
    std::atomic_flag access = ATOMIC_FLAG_INIT; // one byte
    struct
    {
        typedef unsigned short ushort;
        ushort reserved : 8;
        ushort count : 4;
        ushort ready : 1;
        ushort unused : 3;
    } bits;
};

class SpinLock
{
public:
    inline SpinLock(std::atomic_flag &access, bool locked=true)
        : mAccess(access)
    {
        if(locked) lock();
    }
    inline ~SpinLock()
    {
        unlock();
    }
    inline void lock()
    {
        while (mAccess.test_and_set(std::memory_order_acquire))
        {
        }
    }
    // each attempt will take about 10-30 clock cycles
    inline bool try_lock(unsigned int attempts=0)
    {
        while(mAccess.test_and_set(std::memory_order_acquire))
        {
            if (! attempts) return false;
            -- attempts;
        }
        return true;
    }
    inline void unlock()
    {
        mAccess.clear(std::memory_order_release);
    }
private:
    std::atomic_flag &mAccess;
};
void aFn(int &i, Data &d)
{
    SpinLock lock(d.access, false);
    
    // manually locking/unlocking can be tighter
    lock.lock();
    if (d.bits.ready)
    {
        ++d.bits.count;
    }
    d.bits.ready ^= true; // alternate each time
    lock.unlock();
}
int main(void)
{
    Data f;
    f.bits.count = 0;
    f.bits.ready = true;
    std::thread *p[8];
    for (int i = 0; i < 8; ++ i)
    {
        p[i] = new std::thread([&f] (int i) { aFn(i, f); }, i);
    }
    for (int i = 0; i < 8; ++i)
    {
        p[i]->join();
        delete p[i];
    }
    std::cout << "size: " << sizeof(f) << std::endl;
    std::cout << "count: " << f.bits.count << std::endl;
}

结果正如预期的那样...

尺寸: 2
计数: 4

最新更新