结构位域上的 std::atomic

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

我正在修改一些现有的开源库,并且有一个包含位字段的结构(例如名为 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

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

提前致谢!

c++11 concurrency atomic bit-fields
1个回答
0
投票

(1) 您可以使用 这个示例 通过#defining 位来操作原子整数的位。

(2) 使用需要原子性的位域在编程上不太理想,但您可以牺牲 8 位并使用并集在位域中插入

std::atomic_flag
(锁)。

每次访问该结构时都可以手动旋转锁定。但是,代码的性能应该比使用

std::mutex
std::unique_lock
创建、锁定、解锁、销毁更好。此代码可能会浪费大约 10-30 个时钟周期来启用低成本多线程。

PS。确保下面保留的 8 位不会被处理器的字节序结构弄乱。您可能必须在末尾定义大端处理器。我只在 Intel CPU 上测试了这段代码(始终是小端)。

// Use when the operation is quick to avoid mutex locks, 
// but too long to fit into a single atomic operation, 

#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 : 5;
        ushort ready : 1;
        ushort mult : 2;
    } 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);

    // ... some code...

    // manually locking/unlocking can be tighter
    lock.lock();
    if (d.bits.ready)
    {
        ++d.bits.count;
    }
    d.bits.ready ^= true; // alternate each time
    lock.unlock();

    // ... some other code...
}

void aFn2(int &i, Data &d)
{
    // When you need to lock the entire function
    SpinLock lock(d.access);
    if (d.bits.ready)
    {
        d.bits.count += d.bits.mult;
    }
    d.bits.ready ^= true;
    ++ d.bits.mult;
}   // returning will free up the lock as well

int main(void)
{
    Data f;
    f.bits.count = 0;
    f.bits.ready = true;
    f.bits.mult = 1;

    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

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