跳至主要內容

页面配置

张威大约 4 分钟c/c++c++11

基于CAS操作的atomic原子类型

问题: 前面在进行卖票操作时,多线程操作不安全,票的数量Count不正确;

解决方法: 前面是通过lock_gard互斥锁解决的;

int count=100;
lock_guard<std::mutex> guard(mtx);
count++;

lock_guard<std::mutex> guard(mtx);
count--;

这里使用互斥锁存在的问题:

解决方法:

  • 使用CAS保证上面++ --操作的原子特性就足够了,CAS也叫做无锁操作
  • (并不是说不加锁,只是这个加锁不在我们的软件层面,CPU和内存通信是通过系统总线进行的,,当一个线程在做CPU和内存之间数据的交换,也就是说内存有一个数据读到CPU,CPU进行计算再把数据写回内存块的这样一个过程,线程没有做完的话,它是不允许其他线程再去使用总线的,即硬件上实现的加锁操作,对于软件层面可以说是无锁操作

无锁队列 => 就是由CAS来实现的

优点: 有助于提高

C++11的atomic原子类型

#include <atomic>//包含了很多原子类型

yield():线程,等待下一次调度;然后线程处于,等待下一次CPU的调度,重新获取时间片

#include <iostream>
#include <thread>
#include <atomic>	//包含了很多原子类型
#include <list>
using namespace std;

volatile std::atomic_bool isReady = false;
volatile std::atomic_int mycount = 0;

void task()
{
	while (!isReady)//还没有准备好 
	{
		std::this_thread::yield();//线程出让当前的CPU时间片,等待下一次调度
		//线程相当于还是就绪状态,等待CPU的下一次调度 
	}

	for (int i = 0; i < 100; ++i)
	{
		mycount++;
	}
}
int main()
{
	list<std::thread> tlist;
	for (int i = 0; i < 10; ++i)
	{
		tlist.push_back(std::thread(task));//启动10个线程 
	}

	std::this_thread::sleep_for(std::chrono::seconds(3));//主线程睡眠3秒 
	isReady = true;//所有子线程都进行mycount++ 

	for (std::thread& t : tlist)
	{
		t.join();
	}
	cout << "mycount:" << mycount << endl;

	return 0;
}

因为mycount类型就是一个CAS原子操作,如果是普通int,结果就不一定是1000了;

上面数据段的变量不加volatile出现的问题:

  • 一个进程中,不同的线程栈不同,但是堆和数据段都是共享的
  • 共享的变量,多线程会进行缓存,问题就是main线程中的isReady改成true后,其他线程不能立马看到isReady改成true了,

解决方法: 在数据段的变量前面加上,一个线程对共享变量的改变,马上均能反应到另一个线程上。

线程缓存可以加快线程运行的效率,因为线程就像人一样,好不容易从内存上跑到CPU上,它还要不断从内存上取东西做运算,它好不容易占1次CPU的时间片,还要在CPU和内存中不断来回跑,很浪费时间,所以操作系统对线程的执行来说,都是会让线程来CPU执行的时候把它们共享的变量在线程的栈上拷贝一份,统一的都带到CPU的缓存里加个volatile ,就是让所有的线程对共享变量不再进行缓存,保证我们代码的正确性,一个线程对共享变量的改变马上就可以反映到另外一个线程里面了,这就是原子类型。