std::atomic_flag
是原子布尔类型。不同于所有 std::atomic 的特化,它保证是免锁的!!!
它的接口test_and_set(),原子地更改 std::atomic_flag 的状态为 true,并返回它先前的值!!!
(注意无论如何都设置现在状态为TRUE,返回值才区分这次设置有效,还是上次已经有效)
在标头 | ||
bool test_and_set(std::memory_order order = std::memory_order_seq_cst) volatile noexcept; | (1) | (C++11 起) |
bool test_and_set(std::memory_order order = std::memory_order_seq_cst) noexcept; | (2) | (C++11 起) |
这里是演示atomic_flag的消费者、生产者线程(例子中的消费、生产反过来看,不影响理解知识点)
static void scer(int iIndex); //生产者
static void xfer(int idx); //消费者
void test_atomic()
{
const int CNT = 1;
std::thread xfzer[CNT];
std::thread sczer[CNT];
for (int i = 0; i < CNT; i++) {
sczer[i] = std::thread(scer, i+1);
xfzer[i] = std::thread(xfer, 100+i);
}
for (auto& _iter : sczer) {
_iter.join();
}
for (auto& _iter: xfzer) {
_iter.join();
}
}
int m_iNeedXFTimes = 100;
atomic_flag canXfMutex = ATOMIC_FLAG_INIT;
int m_iLastScTimes = 0; //当消费m_iNeedDoTimes次,生产了几次。
void scer(int iIndex)
{
random_device rd;
default_random_engine r_eng(rd());
uniform_int_distribution<int> int_dist(1, 100);
while (m_iNeedXFTimes > 0 && m_iLastScTimes++ < 10000) {
int value = int_dist(r_eng);
//随机控制xf
if (value % 2) {
canXfMutex.clear();
}
printf("sc %2d xf:%d\n", m_iLastScTimes, 100 - m_iNeedXFTimes);
}
std::cout << "sc over " << m_iLastScTimes << "!\n";
}
void xfer(int idx)
{
while (m_iNeedXFTimes > 0) {
//如果之前已经设置成功过了则返回true,反之是这次才设置成功的则返回false;
//请注意,这个接口都是把值成功设置的,返回值true是之前已经设置过了,false是这次设置成功
if (canXfMutex.test_and_set() == false) {
m_iNeedXFTimes--;
}
}
std::cout << "xf over!\n";
}
实例化atomic的原子性说明
atomic 对 bool char int long,指针等数据类型进行原子操作,原子操作不包括浮点数类型以及复合数据类型,主要介绍相关的接收以及使用
(先记录网上文章说的atomic原子操作不包括浮点数,以后做个测试证明浮点数原子操作错误的现象)
这里说的与官网上介绍可原子操作的说明有冲突,官网上介绍
满足五个条件,平凡可赋值,拷贝构造,移动构造,拷贝赋值,移动赋值五个条件
auto r0 = std::is_trivially_copyable<tagAtomic>::value;
auto r1 = std::is_copy_constructible<tagAtomic>::value;
auto r2 = std::is_move_constructible<tagAtomic>::value;
auto r3 = std::is_copy_assignable<tagAtomic>::value;
auto r4 = std::is_move_assignable<tagAtomic>::value;
std::cout << "test atomic:\n" << r0 << " " << r1 << " " << r2 << " " << r3 << " " << r4 << "\n\n";
满足以上五个条件为真,则可原子实例对象
把以上例子的tagAtomic改成以下testWeak_T或testWeak_TT,测试以上五个条件返回值。
class testWeak_T {
public:
//若显式使用析构,则不可平凡复制
//默认析构,则可平凡复制
//~testWeak_T() {
//};
testWeak_T() {
std::cout << "vector class " << this << "\n";
};
//std::list<int> i_member;
int i_member;
};
class testWeak_TT {
public:
testWeak_TT() : i_member(0) {
std::cout << "construct default " << this << "\n";
};
testWeak_TT(int value) : i_member(value) {
std::cout << "construct " << i_member << " - " << this << std::endl;
};
//不可平凡复制
//testWeak_TT(testWeak_TT const& t) {
// i_member = t.i_member;
// std::cout << "copy construct" << this << std::endl;
//};
//不可平凡复制
//testWeak_TT& operator = (const testWeak_TT& t) {
// i_member = t.i_member;
// std::cout << "copy assignment" << this << std::endl;
// return *this;
//};
//不可平凡复制
//testWeak_TT(testWeak_TT&& t) noexcept {
// i_member = t.i_member;
// std::cout << "move construct" << this << std::endl;
//};
//不可平凡复制
//testWeak_TT& operator = (testWeak_TT&& t) {
// std::cout << "move assignment " << this << std::endl;
// i_member = t.i_member;
// return *this;
//}
public:
int i_member;
};
std::atomic实现的原子量到底是使用了原子CPU
指令实现了无锁化,还是依然使用的加锁的方式来实现原子操作,视模板特化类型而定
可根据成员函数is_lock_free查看返回。
is_lock_free的实现代码
#if 1 // TRANSITION, ABI
_NODISCARD bool is_lock_free() const volatile noexcept {
constexpr bool _Result = sizeof(_Ty) <= 8 && (sizeof(_Ty) & sizeof(_Ty) - 1) == 0;
return _Result;
}
#else // ^^^ don't break ABI / break ABI vvv
_NODISCARD bool is_lock_free() const volatile noexcept {
#if _ATOMIC_HAS_DCAS
return sizeof(_Ty) <= 2 * sizeof(void*);
#else // ^^^ _ATOMIC_HAS_DCAS / !_ATOMIC_HAS_DCAS vvv
return sizeof(_Ty) <= sizeof(void*) || (sizeof(_Ty) <= 2 * sizeof(void*) && __std_atomic_has_cmpxchg16b());
#endif // _ATOMIC_HAS_DCAS
}
#endif // TRANSITION, ABI
根据这个实现,可判断以下类型是否免锁
struct tagAtomic_0 {
tagAtomic_0() {
}
int k0;
};
struct tagAtomic_1 {
tagAtomic_1() {
}
int k1_1;
int k1_2;
};
struct tagAtomic_2 {
tagAtomic_2() {
}
int k2_1;
char k2_2;
};
#pragma pack(1)
struct tagAtomic_3 {
tagAtomic_3() {
}
int kk;
char m_a;
};
#pragma pack()
void test_atomic()
{
atomic<tagAtomic_0> myData0;
atomic<tagAtomic_1> myData1;
atomic<tagAtomic_2> myData2;
atomic<tagAtomic_3> myData3;
std::cout << "my atomic_0 is free " << myData0.is_lock_free() << std::endl;
std::cout << "my atomic_1 is free " << myData1.is_lock_free() << std::endl;
std::cout << "my atomic_2 is free " << myData2.is_lock_free() << std::endl;
std::cout << "my atomic_3 is free " << myData3.is_lock_free() << std::endl;
cout << "double atomic is free " << atomic<double>().is_lock_free() << std::endl;
cout << "float atomic is free " << atomic<float>().is_lock_free() << std::endl;
}
输出结果
my atomic_0 is free 1
my atomic_1 is free 1
my atomic_2 is free 1
my atomic_3 is free 0
double atomic is free 1
float atomic is free 1
结果验证了代码分析,让模板特例成员含两个或一个成员并且字节数是2或4或8字节的才能免锁。
(原子cpu指令)