我正在修改一些现有的开源库,并且有一个包含位字段的结构(例如名为 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
任何人都可以建议/了解如何在对现有源代码影响最小的情况下获得原子字段的功能吗?
提前致谢!
(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