C++11多线程:原子操作std::automic-用于多个线程之间共享的变量。

系列文章目录



前言

(1)原子操作std::automic的基本概念和用法。
(2)使用注意事项.


一、std::automic的基本概念

std::atomic来代表原子操作,std::automic是个类模板。其实std::atomic这个东西是用来封装某个类型的值的。

  • 普通变量:

并发的多次访问可能会导致数据竞争,导致操作过程不会按照正确的顺序进行;

  • 原子变量:

确保对共享变量的操作在执行时不会被其他线程的操作干扰,避免竞态条件和死锁等。

1.1 原子操作概念引出范例

互斥量:多线程编程中 保护共享数据:先锁,操作共享数据,开锁

有两个线程,对一个变量进行操作,这个线程读该变量值,另一个线程往这个变量中写值。
int atomvalue = 5//读线程A
int tmpvalue = atomvalue;
//这里这个atomvalue代表的是多个线程之间共享的变量;
//写线程B
atomvalue = 6;

1.2 汇编代码的话;
大家可以把原子操作理解成一种:不需要用到互斥量加锁(无锁)技术的多线程并发编程方式

  • 原子操作:在多线程中 不会被打断的 程序执行片段;原子操作,比互斥量效率上更胜一筹。
  • 互斥量的加锁一般是针对一个代码段(几行代码),而原子操作针对的一般是一个变量,而不是一个代码段;
  • 原子操作:一般都是指“不可分割的操作”;也就是说这种操作状态要么是完成的,要么是没完成的,不可能出现半完成状态;

二、函数成员

store 用非原子参数替换原子对象的值
load 获取原子对象的值
exchange 交换两个原子对象的值
wait 阻塞线程,直到收到通知并且原子值发生变化
notify_one 通知至少一个线程在等待原子对象
notify_all 通知所有阻塞的线程等待原子对象

三、使用步骤

1.代码案例1

代码如下(示例):
基本使用。

#include <iostream>
#include <stdio.h>
#include <tchar.h>
#include <SDKDDKVer.h>
#include <vector>
#include <string>
#include <map>
#include <thread>
#include <list>
#include <mutex>
#include <future>
//int g_mycout = 0; //定义一个全局变量
std::atomic<int> g_mycout = 0; //我们封装了一个类型为int的对象(值);我们可以像操作一个int类型变量一样来操作这个g_mycout

//std::mutex g_my_mutex; //互斥量

void mythread03() //线程入口函数
{
    
    
	for (int i = 0; i < 100000; i++)
	{
    
    
		//g_my_mutex.lock();
		//7秒钟实现了2000万次的加锁和解锁;
		//g_mycout++;
		//...
		//...
		//g_my_mutex.unlock();
		
		g_mycout++;
		//g_mycout = gmycout + 1;  //会导致计数不准确
		
		//cout <<""
		//对应的操作是原子操作(不会被打断)
	}
	
	
	return;
}

int main()
{
    
    
	//三:原子操作std::automic
	//(3.1)原子操作概念引出范例
	//互斥量:多线程编程中 保护共享数据:先锁,操作共享数据,开锁
	//有两个线程,对一个变量进行操作,这个线程读该变量值,另一个线程往这个变量中写值。
	//int atomvalue = 5;
	读线程A
	//int tmpvalue = atomvalue;
	//这里这个atomvalue代表的是多个线程之间共享的变量;
	写线程B
	//atomvalue = 6;
	//汇编代码的话;

	//大家可以把原子操作理解成一种:不需要用到互斥量加锁(无锁)技术的多线程并发编程方式
	//原子操作:在多线程中 不会被打断的 程序执行片段;原子操作,比互斥量效率上更胜一筹。
	//互斥量的加锁一般是针对一个代码段(几行代码),而原子操作针对的一般是一个变量,而不是一个代码段;

	//原子操作:一般都是指“不可分割的操作”;也就是说这种操作状态要么是完成的,要么是没完成的,不可能出现半完成状态;
	//std::atomic来代表原子操作,std::automic是个类模板。其实std::atomic这个东西是用来封装某个类型的值的;
	//(3.2)基本的std::atomic

	std::thread myobj1(mythread03);
	std::thread myobj2(mythread03);
	cout << "线程开始!" << g_mycout<< endl;
	myobj1.join();
	cout << "thread_myobj1 end!" << g_mycout << endl;
	myobj2.join();
	cout << "thread_myobj2 end!" << g_mycout << endl;

	cout << "两个线程执行完毕,最终的g_mycout的结果是:" << g_mycout << endl;

	return 0;
}

运行截图:
在这里插入图片描述

注意:如果这里
一般atomic原子操作,针对++,–,+=,&=,|=,^=是支持的。其他的可能不支持。


2.代码案例2

使用std::automic变量来控制,多个线程的结束,
也可用于计数、统计、计算数据包的发送与接受。

std::atomic<bool> g_ifend = false; //线程退出标记,这里是原子曹组,防止读和写乱套;

void mythread04()
{
    
    
	std::chrono::milliseconds dura(1000);
	//1秒钟
	while (g_ifend == false)
	{
    
    
		//系统没要求线程退出,所以本线程可以干自己想干的事情
		cout << "thread id = " << std::this_thread::get_id() << " 运行中..." << endl;
		std::this_thread::sleep_for(dura);
	}
	cout << "thread id = " << std::this_thread::get_id() << " 运行中结束..." << endl;
	return;
}

int main()
{
    
    
	//心得,一般用于计数或者统计(累计发送出去了多少个数据包,累计接受到了多个数据包;)

	std::thread myobj1(mythread04);
	std::thread myobj2(mythread04);
	std::chrono::milliseconds dura(5000);
	//5秒钟
	std::this_thread::sleep_for(dura);
	g_ifend = true;
	//对原子对象的写操作,让线程自行运行结果;
	myobj1.join();
	myobj2.join();
	cout << "程序执行完毕,退出" << endl;

	return 0;
}

运行截图:
在这里插入图片描述

总结

  • 了解std::automic的基本概念和使用;
  • 了解std::automic与互斥量的区别;
  • 什么是原子操作?
  • 用std::automic控制多个线程的结束。

猜你喜欢

转载自blog.csdn.net/weixin_55491446/article/details/130117072