系列文章目录
前言
(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控制多个线程的结束。