我正在修改一些现有的开源库,并且有一个包含位域的结构(例如,名为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
任何人都可以就如何获得原子字段的功能对现有源代码产生最小影响的建议/想法?
谢谢!
答案 0 :(得分:0)
下面我以无符号短裤为例。
这不是很理想,但是您可以牺牲8位,并用联合在位字段中插入std::atomic_flag
。不幸的是,std::atomic_flag
类型是std::atomic_bool
类型。
此结构可以在每次访问时手动旋转锁定。但是,代码的性能下降应最小(不同于使用std::mutex
和std::unique_lock
创建,锁定,解锁和销毁)。
此代码可能会浪费大约10-30个时钟周期来启用低成本多线程。
PS。确保下面的保留8位没有被处理器的字节序结构弄乱。您可能必须在最后定义big-endian处理器。我只在Intel CPU(始终为little-endian)上测试了此代码。
#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