当前位置: 首页 > news >正文

C++学习笔记(多线程)

Multithreading

  • 1、线程的基本操作
    • 1.1、创建线程
    • 1.2、等待线程和分离线程
    • 1.3、获取线程id
  • 2、互斥锁
  • 3、条件变量
  • 4、例程

1、线程的基本操作

从C++11开始推出关于多线程的库和函数,相比于Linux所配套的资源,C++11提供的函数更加容易理解和操作,对于跨平台编程更有优势。

1.1、创建线程

导入线程库,创建一个新线程,让线程执行某个函数。

#include <thread>using namespace std; void test_function()
{cout << "hello, i am a subthread." << endl;
}int main()
{thread user_thread(test_function);user_thread.join();return 0;
}

可以看出,创建一个线程的时候需要一个函数名(函数地址)作为初始化参数。那么我们使用lambda函数也是可以的。

    thread user_thread([]{cout << "i am a lambda function." <<endl;});user_thread.join();

用类似于函数指针的function也是可以的。

    function<void()> f = []{cout << "function test..." << endl;};thread user_thread(f);user_thread.join();

1.2、等待线程和分离线程

user_thread.join()是为让主线程阻塞等待user_thread这个线程执行完毕。如果使用user_thread.detach()那就是让子线程自己在后台执行。
我在打印"function test…"之前延时一秒,就可以清楚的看出两者的区别了。

int main() 
{ function<void()> f = []{this_thread::sleep_for(chrono::seconds(1));cout << "function test..." << endl;};thread user_thread(f);user_thread.detach(); 		//user_thread.join();return 0; 
}

1.3、获取线程id

每一个线程都有独一无二的id,以便于用户辨别。

std::this_thread::get_id();

2、互斥锁

互斥锁(mutex:Mutual Exclusion Object)是用于确保数据一致性和解决资源竞争的一种方法。被互斥锁锁住的资源,其它线程将无法访问。值得注意的是,这里锁住的并不是代码块,而是资源,即变量,数组之类的东西。
上锁和解锁是配套的操作,否则将导致某一资源永远无法被访问。

#include <iostream>
#include <thread> 
#include <mutex>using namespace std; int temp = 0;
mutex mu;void thread_1()
{mu.lock();temp++;cout << this_thread::get_id() << " " << temp << endl;mu.unlock();
}void thread_2()
{mu.lock();temp++;cout << this_thread::get_id() << " " << temp << endl;mu.unlock();
}int main() 
{ thread thread1(thread_1);thread thread2(thread_2);thread1.join();thread2.join();system("pause");return 0; 
}

其实像这样裸照上锁解锁还是比较少见的,更多的是利用uniuqe_locker这个类来上锁。因为使用这个类提供了需要方法,在离开作用域的时候还会自动解锁,并且支持和条件变量配套使。

void thread_1()
{unique_lock<mutex> locker(mu);temp++;cout << this_thread::get_id() << " " << temp << endl;//不必再解锁了。
}

unique_lock这个类在构造函数中执行上锁操作,在析构函数中执行解锁操作。但它也支持手动上锁和解锁。上锁的方式也有好几种,我们不妨都看一下。
首先要求实例化的时候不要自动上锁。

unique_lock<mutex> locker(mu, std::defer_lock);
  • locker.lock():默认模式,阻塞式上锁。
  • locker.try_lock():尝试获取资源状态,如果被已经被锁住,则返回true,反之返回false。如果不加判断的使用该方法,则可能导致上锁失败。
  • locker.try_lock_for():输入参数是滞留时间,指定时间该资源还是无法访问则返回false。
  • locker.try_lock_until():输入参数是目标时间,目标时间内还是无法访问该资源则返回false。

给个例子简单看看怎么用吧。

void thread_1()
{unique_lock<mutex> locker(mu, defer_lock);//等待一秒钟,如果还无法访问资源则离开if(locker.try_lock_for(chrono::seconds(1))){temp++;cout << this_thread::get_id() << " " << temp << endl;}
}

3、条件变量

条件变量(conditional variable)主要用于协调多线程,经常和互斥锁一起使用。其设有通知机制,分为单个通(notify_one)知和全部通知(notify_all)两种模式,前者通知任意一个线程,后者通知所有线程。

#include <mutex>
#include <condition_variable>
//定义一个条件变量
condition_variable cond;
//定义一个互斥锁
mutex mtu;

定义好条件变量和互斥锁之后,线程就等待该条件变量被唤醒即可。

void thread_test()
{unique_lock<mutex> locker(mtu);cond.wait(locker);
}int main()
{thread myThread(thread_test);//延迟一下,以便测试条件变量的作用this_thread::sleep_for(chrono::seconds(2));//notify_one是通知任意一个线程,notify_all是通知所有线程cond.notify_one();myThread.join();return 0;
}

如果缺乏互斥锁,那就有可能出现竞争等情况。比如该条件变量其实只唤醒了一次,但两个线程却争着被唤醒,以至于两个线程同时被唤醒。
条件变量等待通知(唤醒)的方式也有三种,类比互斥锁的那几种锁方式,便知道如何使用了。这里不再赘述。

  • wait():阻塞式等待。
  • wait_for():输入参数中有一个滞留时间,滞留时间到达之后即便还没有接到通知也会继续执行下去。
  • wait_until():输入参数中有一个目标时间,同理,目标时间到达之后即便还没有接到通知也会继续执行下去。

这里有个点需要知道,如果再通知的那一刻,所有线程都处于忙碌状态,那么该通知就会被忽略。

void thread_test_2()
{unique_lock<mutex> locker(mtu);if(cond.wait_for(locker, chrono::seconds(1)) == cv_status::timeout)cout << "time out" << endl;		//会在1s之后执行这个打印语句elsecout << "i am thread test 2 !" << endl;
}
int main()
{thread myThread2(thread_test_2);this_thread::sleep_for(chrono::seconds(5));cond.notify_all();myThread2.join();system("pause");return 0;
}

如果不仅仅想要判断条件变量,还想同时判断其它条件,可以这样做。

bool isReady = false;
unique_lock<mutex> locker(mtu);
cond.wait(locker, isReady);

当然,传入lambda函数或其它返回值为bool类型的函数也是可以的。

cond.wait(locker, []{cout << "this is lambda function" << endl;return isReady;});

不过有几点比较奇怪的是,你不能在执行cond.wait()语句之前就让函数的返回值是true,否则它会直接终止等待,执行下面的语句。
即你不能这样。

cond.wait(locker, true});

至于wait_for和wait_until如何仿造上面,同时进行多个判断,我也没学明白。。需要时候再学吧。

4、例程

自己写了一个线程池,可能不严谨。
大致思路如下:
1、定义一个任务队列,里面存放N个任务。
2、定义M个线程,存放在vector容器中。让这些线程不断往任务队列中取出任务,并执行之。
3、每添加一个任务进任务队列,就会唤醒任意一个空闲线程去读取任务。
4、用互斥锁去保证任务队列读写任务的顺利执行,并保证条件变量的效果。
5、在析构函数中,即整个程序结束前,不断唤醒空闲线程去取任务,直至任务队列为空。

#include <iostream>
#include <mutex>
#include <thread>
#include <queue>
#include <vector>
#include <functional>
#include <condition_variable>using namespace std;class ThreadPool
{
public:ThreadPool(int size) : _size(size), _isStop(false){//emplace some thread into thread_vectorfor(int i = 0; i < _size; i++)  thread_vector.emplace_back(//using lambda function to execute task[this]{function<void()> thraed_task;//all of threads will keep working until receive stop signal.while(true){unique_lock<mutex> locker(mtu);//waiting until queue is not emptycond.wait(locker, [this]{return _isStop || !task_queue.empty();});if(_isStop && task_queue.empty())   break;thraed_task  = task_queue.front();task_queue.pop();mtu.unlock();thraed_task();}     });}~ThreadPool(){while(!task_queue.empty())cond.notify_all();_isStop = true;for(int i = 0; i < _size; i++)  thread_vector[i].join();cout << "deconstructor" << endl;}void enqueue_task(function<void()> task){//we needn't consider mutex in different resource.unique_lock<mutex> locker(mtu);task_queue.push(move(task));cond.notify_one();mtu.unlock();}private:int _size;bool _isStop;mutex mtu;condition_variable cond;queue<function<void()>> task_queue;vector<thread> thread_vector;
};void HandleTask(int cnt)
{printf("thread %d handle the %d task!\n", this_thread::get_id(), cnt);this_thread::sleep_for(chrono::seconds(1)); // Simulate work
}int main()
{//limit scope{ThreadPool pool(3);for (int i = 0; i < 9; i++) {pool.enqueue_task([i] { HandleTask(i + 1); });}}system("pause");return 0;
}

相关文章:

C++学习笔记(多线程)

Multithreading 1、线程的基本操作1.1、创建线程1.2、等待线程和分离线程1.3、获取线程id 2、互斥锁3、条件变量4、例程 1、线程的基本操作 从C11开始推出关于多线程的库和函数&#xff0c;相比于Linux所配套的资源&#xff0c;C11提供的函数更加容易理解和操作&#xff0c;对…...

解决Redis的键值前出现类似\xAC\xED\x00\x05t\x00*这样的字符序列

文章目录 1.问题2.解决方法3.StringRedisTemplate和RedisTemplate的区别 1.问题 在使用RedisTemplate对Redis进行操作时,发现Reids键值对前有\xAC\xED\x00\x05t\x00*这样的字符序列 如图所示: 虽说不影响使用,但是听影响观感的 2.解决方法 查找了很多方法,可以指定RedisTem…...

分享 Kamailio 5.7.x 预处理一例

来自工单&#xff0c;很不错 不翻译了&#xff0c;认真看的话都能看懂 #!define IPADDR 127.0.0.1 #!defexp SIPURI "sip:" IPADDR ":5060" #!defexp QSIPURI "sip: IPADDR :5060" #!defexp V16 1<<4 Another possibility is using…...

学QT的第三天~

ikun登录界面完善 #include "mywidget.h" void MyWidget::bth1() { if(edit3 ->text()"520cxk"&&edit4 ->text()"1314520") { //1.实例化一个QmessageBox类的对象 QMessageBox box(QMessageBox::Information, //图标 "恭喜…...

数据结构---时间复杂度+空间复杂度

算法(algorithm)简单说就是解决问题的方法。方法有好坏&#xff0c;同样算法也是&#xff0c;有效率高的算法&#xff0c;也有效率低的算法。衡量算法的好坏一般从时间和空间两个维度衡量&#xff0c;也就是本文要介绍的时间复杂度和空间复杂度。有些时候&#xff0c;时间与空间…...

Verilog 触发器状态机语言描述

触发器状态机语言描述 触发器状态机语言用于描述映射到 ILA 调试核的高级触发器逻辑的复杂触发条件。触发器状态机具有下列特性 &#xff1a; • 最多 16 种状态。 • 用于复杂状态转换的单向、双向和三向条件分支。 • 4 个内置 16 位计数器 &#xff0c; 用于对事件…...

等保保护测评试题中

二、多选题 1、防火墙提供的接入模式中包括&#xff08;ABCD&#xff09; A.网关模式 B.透明模式 C.混合模式 D.旁路接入模式 2、不同设VLAN之间要进行通信&#xff0c;可以通过 .&#xff08;AB&#xff09; A.交换机 B.路由器 C.网闸 D.入侵检测 E.入侵防御系统…...

SD-Turbo部署

stabilityai/sd-turbo 官网 2023 年 11 月 30 日 继推出 SDXL-Turbo 之后&#xff0c;我们又发布了SD-Turbo。 2023 年 11 月 28 日 我们正在发布 SDXL-Turbo&#xff0c;一种闪电般快速的文本到图像模型。除了模型之外&#xff0c;我们还发布了技术报告 用法&#xff1…...

【ZZULIOJ】1095: 时间间隔(函数专题)(Java)

目录 题目描述 输入 输出 样例输入 Copy 样例输出 Copy 提示 code 题目描述 从键盘输入两个时间点(24小时制&#xff09;&#xff0c;输出两个时间点之间的时间间隔&#xff0c;时间间隔用“小时:分钟:秒”表示。要求程序定义如下两个函数&#xff0c;并在main()中调用…...

Rust:文件 launch.json 有什么用?

launch.json 是 Visual Studio Code&#xff08;VSCode&#xff09;中的一个配置文件&#xff0c;主要用于配置调试器。当你在 VSCode 中进行代码调试时&#xff0c;launch.json 文件告诉调试器如何启动和配置你的程序。 具体来说&#xff0c;launch.json 文件包含了以下信息&…...

vue3实现文字垂直滚动

在Vue 3中实现文字的垂直滚动&#xff0c;你可以使用CSS动画或者JavaScript来控制滚动行为。以下是一个简单的Vue 3组件示例&#xff0c;该组件使用CSS的keyframes动画来实现文字的垂直滚动效果&#xff1a; <template> <div class"vertical-scroll-text"&…...

Android4.4真机移植过程笔记(三)

如果文章字体看得不是很清楚&#xff0c;大家可以下载pdf文档查看&#xff0c;文档已上传&#xff5e;oo&#xff5e; 7、安装加密APK 需要修改文件如下&#xff1a; 相对Android4.2改动还是蛮大的&#xff0c;有些文件连路径都变了: //Android4.2 1、frameworks/native/libs…...

PostgreSQL备份恢复与复制

前言 随着国家战略层面对信息安全关注度越来越高&#xff0c;数据库是基础软件国产化自主可控的重要方面之一。PG是世界上最流行的开源关系型数据库之一&#xff0c;并且他是类BSD开源许可&#xff0c;开源协议非常友好&#xff0c;可以随意分发、闭源和开源&#xff0c;可以用…...

spring高级篇(八)

本篇对Spring MVC 的执行流程做一个简单总结 MVC执行流程总结 当浏览器发送一个请求&#xff0c;例如http://localhost:8080/hello&#xff0c;请求到达服务器后&#xff0c;一般会进行如下操作&#xff1a; 1、首先会经过DispatcherServlet&#xff0c;默认映射路径为 /&…...

UP互助 帮助UP起号做视频 支持B站和抖音

【软件名字】&#xff1a;UP互助 【软件版本】&#xff1a;1.0 【软件大小】&#xff1a;17.5MB 【软件平台】&#xff1a;安卓 【测试机型】&#xff1a;小米9 1.随便登个邮箱&#xff0c;添加自己平台的频道&#xff0c;然后就可以帮助别人&#xff0c;添加频道后在添加…...

*求问?:为何会超时(TLE)?

D - Grid and Magnet (atcoder.jp) 错误代码&#xff1a; //2024年5月5日14:53:43 #include <bits/stdc.h> #define move mmove //防止与头文件中重复 using namespace std; int h,w; string s[1000]; const int move[4][2]{{1,0},{-1,0},{0,1},{0,-1}}; bool used[100…...

cocosstudio工程文件(.ccs)维护问题

创建cocos工程.bat在多人合作的cocos项目中&#xff0c;大家公用一个ccs文件&#xff0c;存在的问题是如果大家都提交ccs文件比较容易出现冲突&#xff0c;解决冲突麻烦要耗费时间&#xff0c;不提交的话就拉不到其他人更新的csd文件。 方案一 解决冲突&#xff0c;更新提交c…...

Blender动画与云渲染:创造高质量作品的未来路径

Blender作为开源的3D图形软件&#xff0c;在多个领域广受欢迎。但随着项目复杂度提升&#xff0c;传统渲染方式受限。云渲染技术的兴起突破了这些限制&#xff0c;为创作者提供了更自由、高效的创作环境。 一、Blender动画项目的挑战 传统上&#xff0c;Blender动画渲染需要依…...

【MySQL】3.MySQL核心概念解析:数据完整性、事务处理、索引及聚簇索引与非聚簇索引

探索MySQL的内部机制&#xff0c;理解数据完整性、事务处理、索引策略以及聚簇索引与非聚簇索引的区别是至关重要的。这些概念构成了数据库设计和优化的基础&#xff0c;对于确保数据的准确性、提高查询效率、维护数据的一致性和实现复杂的数据库操作至关重要。本文将逐一剖析这…...

【netty系列-03】深入理解NIO的基本原理和底层实现(详解)

Netty系列整体栏目 内容链接地址【一】深入理解网络通信基本原理和tcp/ip协议https://zhenghuisheng.blog.csdn.net/article/details/136359640【二】深入理解Socket本质和BIOhttps://zhenghuisheng.blog.csdn.net/article/details/136549478【三】深入理解NIO的基本原理和底层…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...