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