当前位置: 首页 > 工具软件 > Atomic > 使用案例 >

atomic、atomic_flag及无锁的原子指令的理解

罗安宁
2023-12-01

atomic_flag介绍:

std::atomic_flag 是原子布尔类型。不同于所有 std::atomic 的特化,它保证是免锁的!!!

它的接口test_and_set(),原子地更改 std::atomic_flag 的状态为 true,并返回它先前的值!!!

(注意无论如何都设置现在状态为TRUE,返回值才区分这次设置有效,还是上次已经有效)

在标头 <atomic> 定义

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";
}

std::atomic介绍

实例化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指令)

 类似资料: