并发 多线程

目录

thread

thread 类总览

构造函数

= join joinable

​编辑

detach swap yield swap

成员函数的调用

namespace::this_thread

线程同步--锁

互斥锁mutex

递归锁recursive_mutex

定时锁

Lock 锁辅助类

lock_guard​编辑

unique_lock

std::lock 解决死锁问题

消息队列

condition_variable

condition_variable

生产者消费者模型

atomic

call_once

​chrono 时间库


thread

thread 类总览

构造函数

= join joinable

  • 谁调用了这个函数?调用了这个函数的线程对象,一定要等这个线程对象的方法(在构造时传入的方法)执行完毕后(或者理解为这个线程的活干完了!),这个join()函数才能得到返回。

  • 在什么线程环境下调用了这个函数?上面说了必须要等线程方法执行完毕后才能返回,那必然是阻塞调用线程的,也就是说如果一个线程对象在一个线程环境调用了这个函数,那么这个线程环境就会被阻塞,直到这个线程对象在构造时传入的方法执行完毕后,才能继续往下走,另外如果线程对象在调用join()函数之前,就已经做完了自己的事情(在构造时传入的方法执行完毕),那么这个函数不会阻塞线程环境,线程环境正常执行

detach swap yield swap

成员函数的调用

必须传入成员函数地址和调用的对象

namespace::this_thread

线程同步--锁

互斥锁mutex

递归锁recursive_mutex

递归锁出现的意义:假设存在这样一个场景,一个函数使用mutex 同时调用另外的一个函数里面有用到同一个mutex,则此时同一个互斥量被上了两次锁,导致死锁;而递归锁可以对互斥量拥有多层所有权,可以避免死锁;

同一个线程多次占用(递归占用次数有限,不能太多),可配合lock_guard使用,不通线程和互斥锁一致。

定时锁

Lock 锁辅助类

lock_guard

unique_lock

比lock_guard更灵活,主要用与条件变量一同使用

std::lock 解决死锁问题

 

消息队列

#include <list>
#include <thread>
#include <mutex>
#include<iostream>
​
class A
{
public://把收到的消息(玩家命令)入到一个队列的线程void inMsgRecvQueue(){for (int i = 0;i < 100;++i){cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;my_mutex.lock();msgRecvQueue.push_back(i);my_mutex.unlock();}}
​bool outMsgLULProc(int& command){// my_mutex.lock();std::lock_guard<std::mutex> sguard(my_mutex);if (!msgRecvQueue.empty()){command = msgRecvQueue.front();msgRecvQueue.pop_front();// my_mutex.unlock();return true;}// my_mutex.unlock();return false; }
​//把数据从消息队列中取出的线程void outMsgRecvQueue(){int command = 0;for (int i = 0;i < 100;i++){bool result = outMsgLULProc(command);if (result == true){cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;//可以考虑对命令(数据)进行处理}else{cout << "outMsgRecvQueue()执行,但是目前消息队列中为空" << i << endl;}}cout << "end" << endl;}
​
private:std::list<int> msgRecvQueue;    //容器,专门用于代表玩家发送过来的命令std::mutex my_mutex; //创建一个互斥量 
};
​
int main()
{A myobja;std::thread myOutnMsgObj(&A::outMsgRecvQueue,&myobja);std::thread myInMsgObj(&A::inMsgRecvQueue,&myobja);myInMsgObj.join();myOutnMsgObj.join();return 0;
}

condition_variable

condition_variable

生产者消费者模型

//condition_variable.h头文件#include <mutex>
#include <thread>
#include <chrono>
#include <deque>
#include <condition_variable>
​
namespace TestConditional_variable
{extern std::mutex g_cvMutex;extern std::condition_variable g_cv;extern std::deque<int>g_data_deque;extern const int MAX_NUM;extern int g_next_index;
​const int PRODUCER_THREAD_NUM = 3;const int CONSUMER_THREAD_NUM = 3;
​void producer_thread(int thread_id);void consumer_thread(int thread_id);
​
}//condition_variable.cpp头文件#include"Condition_variable.h"
#include<iostream>
namespace TestConditional_variable {
​std::mutex g_cvMutex;std::condition_variable g_cv;std::deque<int>g_data_deque;const int MAX_NUM = 30;
​int g_next_index = 0;void producer_thread(int thread_id){for (int i = 0; i < 4;i++){std::this_thread::sleep_for(std::chrono::milliseconds(500));std::unique_lock<std::mutex>lk(g_cvMutex);g_cv.wait(lk, [](){return g_data_deque.size() <= MAX_NUM; });//wait的第二个参数为可执行的OBJ 反复执行直到返回trueg_next_index++;g_data_deque.push_back(g_next_index);std::cout << "producer_thread" << thread_id << " producer data" << g_next_index;std::cout << " queue size:" << g_data_deque.size() << std::endl;g_cv.notify_all();}}
​void consumer_thread(int thread_id){for (int i = 0; i < 4; i++){std::this_thread::sleep_for(std::chrono::milliseconds(500));std::unique_lock<std::mutex>lk(g_cvMutex);g_cv.wait(lk, [](){return !g_data_deque.empty(); });g_next_index++;int data = g_data_deque.front();g_data_deque.pop_front();std::cout << "consumer_thread" << thread_id << " consumer data" << g_next_index;std::cout << " queue size:" << g_data_deque.size() << std::endl;g_cv.notify_all();}}
​
}#include"Condition_variable.h"
#include<iostream>
​
int main(int argc, char *argv[])
{std::thread arrProducerThread[TestConditional_variable::PRODUCER_THREAD_NUM];std::thread arrConsumerThread[TestConditional_variable::CONSUMER_THREAD_NUM];
​for (int i = 0; i < TestConditional_variable::PRODUCER_THREAD_NUM; i++){arrProducerThread[i] = std::thread(TestConditional_variable::producer_thread, i);}for (int i = 0; i < TestConditional_variable::CONSUMER_THREAD_NUM; i++){arrConsumerThread[i] = std::thread(TestConditional_variable::consumer_thread, i);}for (int i = 0; i < TestConditional_variable::PRODUCER_THREAD_NUM; i++){arrProducerThread[i].join();}for (int i = 0; i < TestConditional_variable::CONSUMER_THREAD_NUM; i++){arrConsumerThread[i].join();}return 0;
}

atomic

原子变量

call_once

        在某些特定情况下,某些函数只能在多线程环境下调用一次,比如:要初始化某个对象而这个对象只能被初始化一次,就可以使用 std::call_once() 来保证函数在多线程环境下只能被调用一次。使用 call_once() 的时候,需要一个 once_flag 作为 call_once() 的传入参数。

           void call_once( std::once_flag& flag, Callable&& f, Args&&... args );

          flag:once_flag 类型的对象,要保证这个对象能够被多个线程同时访问到。

          f:回调函数,可以传递一个有名函数地址,也可以指定一个匿名函数

         args:作为实参传递给回调函数。

   

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;once_flag g_flag;
void do_once(int a, string b)
{cout << "name: " << b << ", age: " << a << endl;
}void do_something(int age, string name)
{static int num = 1;call_once(g_flag, do_once, 19, "luffy");cout << "do_something() function num = " << num++ << endl;
}void test_call_once()
{thread t1(do_something, 20, "ace");thread t2(do_something, 20, "sabo");thread t3(do_something, 19, "luffy");t1.join();t2.join();t3.join();
}

chrono 时间库

duration

定义于头文件 <chrono>
template<
    class Rep,
    class Period = std::ratio<1>
> class duration;

这是一个数值类型,表示时钟数(周期)的类型(默认为整形)。若 Rep 是浮点数,则 duration 能使用小数描述时钟周期的数目
Period:表示时钟的周期,它的原型如下
// 定义于头文件 <ratio>
template<
    std::intmax_t Num,//周期的分子
    std::intmax_t Denom = 1//周期的分母 默认为1
> class ratio;

ratio 类表示每个时钟周期的秒数,其中第一个模板参数 Num代表分子,Denom代表分母,该分母值默认为 1,因此,ratio代表的是一个分子除以分母的数值,比如:ratio<2> 代表一个时钟周期是 2 秒,ratio<60 > 代表一分钟,ratio<60*60 > 代表一个小时,ratio<60*60*24 > 代表一天。而 ratio<1,1000 > 代表的是 1/1000 秒,也就是 1 毫秒,ratio<1,1000000 > 代表一微秒,ratio<1,1000000000 > 代表一纳秒。

void test_chrono()
{using namespace std;chrono::hours h(1);                          // 一小时chrono::milliseconds ms{ 3 };                // 3 毫秒  初始化操作 ms{3} 表示一共有 3 个时间周期,每个周期为 1 毫秒std::chrono::microseconds us = 2 * ms;     // 6000 微秒chrono::duration<int, ratio<1000>> ks(3);    // 3000 秒// chrono::duration<int, ratio<1000>> d3(3.5);  // error//dd(6.6) 时钟周期为默认的 1 秒,共有 6.6 个时钟周期,所以 dd 表示的时间间隔为 6.6 秒chrono::duration<double> dd(6.6);               // 6.6 秒 周期类型为小数// 使用小数表示时钟周期的次数//hz(3.5) 时钟周期为 1 / 30 秒,共有 3.5 个时钟周期,所以 hz 表示的时间间隔为 1 / 30 * 3.5 秒chrono::duration<double, std::ratio<1, 30>> hz(3.5);//count统计周期数std::cout << "3 ms duration has " << ms.count() << " ticks\n"  //3<< "6000 us duration has " << us.count() << " ticks\n"     //6000<< "3.5 hz duration has " << hz.count() << " ticks\n";     //3.5//重载了++ -- + - = 等操作chrono::minutes t1(2);chrono::seconds t2(2);chrono::seconds t3 = t1 - t2;chrono::seconds t4 = t1 + t2;t4++;cout << t3.count() <<"seconds" << endl;//118secondscout << t4.count() << "seconds" << endl;//123seconds/*注意事项:duration 的加减运算有一定的规则,当两个 duration 时钟周期不相同的时候,会先统一成一种时钟,然后再进行算术运算,统一的规则如下:假设有 ratio<x1,y1> 和 ratio<x2,y2 > 两个时钟周期,首先需要求出 x1,x2 的最大公约数 X,然后求出 y1,y2 的最小公倍数 Y,统一之后的时钟周期 ratio 为 ratio<X,Y>*/chrono::duration<double, ratio<9, 7> >t5(3);//3*   9/7秒chrono::duration<double, ratio<4, 3>> t6(3); // 3 *   4/3秒chrono::duration<double, ratio<1, 21>> t7 = t6 - t5;cout << t7.count() << "ticks" << endl;//3ticks}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/35421.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅谈逻辑控制器之随机顺序控制器

浅谈逻辑控制器之随机顺序控制器 随机顺序控制器&#xff08;Random Order Controller&#xff09;作为一个独特的逻辑控制器&#xff0c;为测试脚本的执行增添了一层随机性&#xff0c;特别适用于模拟用户行为中不确定的访问模式。 随机顺序控制器概述 随机顺序控制器&…

代码随想录算法训练营第三十六天|62.不同路径、 63. 不同路径 II、343.整数拆分(可跳过)、96.不同的二叉搜索树(可跳过)

62.不同路径 题目链接&#xff1a;62.不同路径 文档讲解&#xff1a;代码随想录 状态&#xff1a;还行 思路&#xff1a;当前状态的只有可能是从上面或者左边过来的&#xff0c;所以 dp[i][j] dp[i-1] dp[j-1] 题解&#xff1a; public int uniquePaths(int m, int n) {if (…

Docker 安装Nginx部署网站 防火墙端口 数据卷挂载

拉取镜像 docker pull nginx#不写版本号 表示最新版本查看是否拉取成功 docker images#成功 nginx latest 605c77e624dd 2 years ago 141MB mysql 8.0 3218b38490ce 2 years ago 516MB mysql latest 3218b38490ce 2 years ago 5…

virtualbox(7.0) ubuntu(22) 和win11共享文件夹

在虚拟机中安装增强功能。在virtualbox中配置 执行命令将用户加入vboxsf组 sudo adduser your_usrname vboxsf 重启ubuntu即可

LeetCode 585, 438, 98

目录 585. 2016年的投资题目链接表要求知识点思路代码 438. 找到字符串中所有字母异位词题目链接标签思路代码 98. 验证二叉搜索树题目链接标签合法区间思路代码 中序遍历思路代码 585. 2016年的投资 题目链接 585. 2016年的投资 表 表Insurance的字段为pid、tiv_2015、tiv…

Charles网络抓包工具手机抓包配置(二)

目录 事前配置 配置手机连接 代理设置 Https请求设置 手机安装根证书 手机连接代理 证书获取​编辑 证书安装 成果 前言-与正文无关 ​ 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&#xff0c;我们…

CesiumJS【Basic】- #020 加载glb/gltf文件(Primitive方式)

文章目录 加载glb/gltf文件(Primitive方式)1 目标2 代码实现3 资源文件加载glb/gltf文件(Primitive方式) 1 目标 使用Primitive方式加载glb/gltf文件 2 代码实现 import * as Cesium from "cesium";const viewer = new Cesium.Viewer

ElementPlus Combogrid 组件

效果图: 1.声明 Props类型 export type comboGridPropType { modelValue: any; url: string; keyField?: string; labelField?: string; filterOptions?: Array<ISearchOption>; tableColumns?: Array<TableColumns>; enableField?: string; multiple?: …

Psychtoolbox 脑电实验范式之mp4视频绘制

1. 读取 首先需要使用到Screen(‘OpenMovie’)函数&#xff0c;该函数可以读取mp4、gif格式的数据&#xff0c;具体方式如下&#xff1a; clear; clc; Screen(Preference, SkipSyncTests, 1); screens Screen(Screens); screenNum max(screens); [window, screenRect] Scr…

C++语法20 一维数组及其相关问题详解

这是《C算法宝典》语法入门篇的第20节文章啦~ 如果你之前没有太多C基础&#xff0c;请点击&#x1f449;专栏&#xff1a;C语法入门&#xff0c;如果你C语法基础已经炉火纯青&#xff0c;则可以进阶算法&#x1f449;专栏&#xff1a;算法知识和数据结构&#x1f449;专栏&…

串口通信中字符和16进制显示的区别分析,串口发送数据

分析的原因 在对串口传送数值时&#xff0c;不想再进行一步字符转化&#xff0c;想要直接传送数值。但查看时就需要以16进制的数值形式查看数据&#xff0c;否则将不能看到正确的数据显示 下图是对串口寄存器发送一个16bit位的数据 void uart0Senduint16(UINT16 dat){SBUFdat…

element-plus 日期选择添加确定按钮

需求&#xff1a;选择日期后&#xff0c;点击确定按钮关闭面板 思路&#xff1a; 使用shortcuts自定义确定和取消按钮选择日期后使用handleOpen()强制开启面板点击确定后使用handleClose()关闭面板 <template><el-date-pickerref"pickerRef"v-model"…

【Android11】开机启动日志捕捉服务

一、前言 制作这个功能的原因是客户想要自动的记录日志中的报错和警告到设备的内存卡里面。虽然开发者模式中有一个“bug report” 会在/data/user_de/0/com.android.shell/files/bugreports/目录下生成一个zip包记录了日志。但是客户觉得这个日志很难获取到他们需要的信息&am…

基于盲信号处理的声音分离——基于自然梯度的ICA算法

基于自然梯度的ICA算法主要利用相互独立的随机信号的联合概率密度是各自概率密度的乘积这一特性&#xff0c;建立了等独立性度量原则&#xff0c;具体实现如下。 首先&#xff0c;输出信号 相互独立&#xff0c;则其概率密度满足 上式中 表示 的概率密度函数&#xff0c;可以…

怎么扫描图片变成pdf格式?办公人士值得收藏的宝藏工具

将图片扫描并转换为PDF格式可以通过多种途径实现&#xff0c;无论是使用专业的扫描仪还是智能手机&#xff0c;都有相应的方法。 PDF 是什么&#xff1f; PDF&#xff0c;全称为 Portable Document Format&#xff08;便携式文档格式&#xff09;&#xff0c;是由Adobe System…

12,SPI

Flash芯片&#xff1a;W25Q64&#xff0c;可以看成一个储存器 W25Q64芯片和单片机之间的通信方式是SPI SPI:串行同步全双工&#xff0c;主从通信 判断一个设备是不是SPI通信&#xff0c;看是否有这几个线&#xff1a;SCK&#xff0c;CS&#xff0c;MISO&#xff0c;MOSI SCK…

Altium Designer软件下载安装「PCB设计软件」安装包资源获取

Altium Designer作为一款集成化的电子产品开发系统&#xff0c;它主要适用于Windows操作系统&#xff0c;为电子设计工程师们提供了一个高效、便捷的工作平台。 在Altium Designer中&#xff0c;原理图设计、电路仿真、PCB绘制编辑、拓扑逻辑自动布线、信号完整性分析和设计输出…

vuex的学习

vuex vuex是个插件&#xff0c;用于多个组件操作共享变量 引入&#xff1a;数字操作案例 基于组件自定义事件而实现的操作数字案例如下&#xff1a; App.vue <template><div id"app"><input class"num_input" type"text" v…

LeeCode 994. 腐烂的橘子

原题链接994. 腐烂的橘子 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;采用bfs遍历图&#xff0c;将烂橘子加入队列&#xff0c;然后将被烂橘子感染的橘子也加入队列&#xff0c;bfs的具体细节就不多说了&#xff0c;可以自己去搜&#xff0c;很简单&#xff0c;…

560.滑动窗口最大值

滑动窗口最大值 239. 滑动窗口最大值 - 力扣&#xff08;LeetCode&#xff09; 题目大意&#xff0c;返回每个窗口内的最大值。 思路-优先队列 优先队列&#xff08;堆&#xff09;&#xff0c;其中的大根堆可以实时维护一系列元素中的最大值。 每当我们向右移动窗口时&#…