C++多线程学习(一):C++11 多线程快速入门
参考引用
- C++11 14 17 20 多线程从原理到线程池实战
- 代码运行环境:Visual Studio 2019
1. 为什么要用多线程
- 任务分解
- 耗时的操作,任务分解,实时响应
- 数据分解
- 充分利用多核CPU处理数据
- 数据流分解
- 读写分离,解耦合设计
2. 第一个子线程代码示例
- first_thread.cpp
#include <iostream> #include <thread>using namespace std;// 创建的子线程的入口函数 void ThreadMain() {// 获取子线程开始时刻的 ID 号cout << "begin sub thread ID " << this_thread::get_id() << endl;for (int i = 0; i < 5; i++) {cout << "in thread " << i << endl;// 子线程睡眠(释放)CPU 资源 1000msthis_thread::sleep_for(chrono::seconds(1)); }cout << "end sub thread ID " << this_thread::get_id() << endl; }// 主线程的入口函数 main() int main(int argc, char* argv[]) {cout << "main thread ID " << this_thread::get_id() << endl;// 线程创建启动thread th(ThreadMain);cout << "begin wait sub thread" << endl;// 阻塞等待子线程退出th.join();cout << "end wait sub thread" << endl;return 0; } - 控制台输出
main thread ID 21580 begin wait sub thread begin sub thread ID 22924 in thread 0 in thread 1 in thread 2 in thread 3 in thread 4 end sub thread ID 22924 end wait sub thread
3. std::thread 对象生命周期、线程等待和分离
-
thread_detach.cpp
#include <iostream> #include <thread>using namespace std;bool is_exit = false;void ThreadMain() {cout << "begin sub thread ID " << this_thread::get_id() << endl;for (int i = 0; i < 5; i++) {if (!is_exit)break;cout << "in thread " << i << endl;this_thread::sleep_for(chrono::seconds(1)); // 子线程睡眠(释放)CPU 资源 1000ms}cout << "end sub thread ID " << this_thread::get_id() << endl; }int main(int argc, char* argv[]) {{//thread th(ThreadMain); // 出错,thread 对象被销毁 子线程还在运行}{thread th(ThreadMain);th.detach(); // 子线程与主线程分离 守护线程(在后台运行)// 但存在一个问题:主线程退出后 子线程不一定退出}{thread th(ThreadMain);this_thread::sleep_for(chrono::seconds(1));//1000msis_exit = true; // 通知子线程退出cout << "主线程阻塞,等待子线程退出" << endl;th.join(); // 主线程阻塞,等待子线程退出cout << "子线程已经退出!" << endl;}getchar();return 0; } -
控制台输出
begin sub thread ID 25520 end sub thread ID 25520 begin sub thread ID 23324 end sub thread ID 23324 主线程阻塞,等待子线程退出 子线程已经退出!
4. 全局函数作为线程入口
-
thread_para.cpp
#include <iostream> #include <thread> #include <string>using namespace std;class Para { public:Para() {cout << "Create Para" << endl;}Para(const Para& p) { // 拷贝构造函数cout << "Copy Para" << endl; }~Para() {cout << "Drop Para" << endl;}string name; };void ThreadMain(int p1, float p2, string str, Para p4) {this_thread::sleep_for(100ms);cout << "ThreadMain " << p1 << " " << p2 << " " << str << " " << p4.name << endl; }int main(int argc, char* argv[]) {thread th;{float f1 = 12.1f;Para p;p.name = "test Para class";// 所有的参数做复制th = thread(ThreadMain, 101, f1, "test string para", p);}th.join();return 0; } -
控制台输出
Create Para Copy Para Drop Para Copy Para ThreadMain 101 12.1 test string para Drop Para Drop Para
5. 线程函数传递指针和引用
-
参数传递存在的问题
- 传递空间已经销毁
- 多线程共享访问一块空间
- 传递的指针变量的生命周期小于线程
-
thread_para.cpp
#include <thread>
#include <iostream>
#include <string>using namespace std;class Para {
public:Para() { cout << "Create Para" << endl; }Para(const Para& p) { cout << "Copy Para" << endl; }~Para() { cout << "Drop Para" << endl; }string name;
};void ThreadMain(int p1, float p2, string str, Para p4) {this_thread::sleep_for(100ms);cout << "ThreadMain " << p1 << " " << p2 << " " << str <<" "<<p4.name<< endl;
}void ThreadMainPtr(Para* p) {this_thread::sleep_for(100ms);cout << "ThreadMainPtr name = " << p->name << endl;
}void ThreadMainRef(Para& p) {this_thread::sleep_for(100ms);cout << "ThreadMainPtr name = " << p.name << endl;
}
int main(int argc, char* argv[]) {{// 传递引用Para p;p.name = "test ref";thread th(ThreadMainRef, ref(p));th.join();}getchar();{// 传递线程指针Para p;p.name = "test ThreadMainPtr name";thread th(ThreadMainPtr, &p); th.detach(); // 错误,线程访问的 p 空间会提前释放}getchar(); // Para 释放{ // 传递线程指针Para p;p.name = "test ThreadMainPtr name";thread th(ThreadMainPtr, &p);th.join();getchar();}thread th;{float f1 = 12.1f;Para p;p.name = "test Para class";// 所有的参数做复制th = thread(ThreadMain, 101, f1, "test string para", p);}th.join();return 0;
}
- 控制台输出
Create Para ThreadMainPtr name = test ref Drop Para
6. 成员函数作为线程入口–封装线程基类接口
-
XThread.h
- 封装线程基类接口
#pragma once#include <iostream> #include <thread>class XThread { public:virtual void Start() {is_exit_ = false;th_ = std::thread(&XThread::Main, this);}virtual void Stop() {is_exit_ = true;Wait();}virtual void Wait() {if (th_.joinable())th_.join();}bool is_exit() {return is_exit_;}private:virtual void Main() = 0; // 纯虚函数必须要在基类中实现std::thread th_;bool is_exit_ = false; // 符合谷歌代码风格,私有成员变量后缀加 _ }; -
thread_class.cpp
#include <iostream> #include <thread> #include <string> #include "XThread.h"using namespace std;/* class MyThread { public:// 入口线程函数void Main() {cout << "MyThread Main" << name << ": " << age;}string name;int age = 0; }; */class TestXThread : public XThread { public:void Main() override {cout << "TestXThread Main begin" << endl;while (!is_exit()) {this_thread::sleep_for(100ms);cout << "." << flush; // 添加 flush 是为了确保 . 正常输出}cout << "\nTestXThread Main end" << endl;}string name; };int main(int argc, char* argv[]) {TestXThread testth;testth.name = "TestXThread name ";testth.Start();this_thread::sleep_for(3s);testth.Stop();testth.Wait();getchar();/*MyThread myth;myth.name = "Test name";myth.age = 20;thread th(&MyThread::Main, &myth);th.join();*/return 0; } -
控制台输出
TestXThread Main begin ............................ TestXThread Main end
7. lambda 临时函数作为线程入口
-
lambda 函数基本格式
- [捕捉列表] (参数) mutable -> 返回值类型 {函数体}
-
thread_lambda.cpp
#include <iostream> #include <thread> #include <string>using namespace std;class TestLambda { public:void Start() {thread th([this]() {cout << "name = " << name << endl; });th.join();}string name = "Test Lambda"; };int main(int argc, char* argv[]) {thread th([](int i) {cout << "test lambda " << i << endl;}, 123);th.join();TestLambda test;test.Start();return 0; } -
控制台输出
test lambda 123 name = Test Lambda
8. call_once 多线程调用函数只进入一次
初始化函数可以在每一个类型的构造里都调用一遍,不用明确区分,代码可读性提升
-
call_once.cpp
#include <iostream> #include <thread> #include <string> #include <mutex>using namespace std;void SystemInit() {cout << "Call SystemInit" << endl; }void CallOnceSystemInit() {static std::once_flag flag; // 通过 flag 区分是否只调用一次std::call_once(flag, SystemInit); }int main(int argc, char* argv[]) {CallOnceSystemInit();CallOnceSystemInit();for (int i = 0; i < 3; i++) {thread th(CallOnceSystemInit);th.detach();}getchar();return 0; } -
控制台输出
Call SystemInit
相关文章:
C++多线程学习(一):C++11 多线程快速入门
参考引用 C11 14 17 20 多线程从原理到线程池实战代码运行环境:Visual Studio 2019 1. 为什么要用多线程 任务分解 耗时的操作,任务分解,实时响应 数据分解 充分利用多核CPU处理数据 数据流分解 读写分离,解耦合设计 2. 第一个…...
Linux系统之lsof命令的基本使用
Linux系统之lsof命令的基本使用 一、lsof命令的基本使用二、lsof命令的使用帮助2.1 lsof命令的help帮助信息2.2 lsof命令帮助解释 三、lsof的基本使用3.1 直接使用lsof命令3.2 查看某个进程打开的所有文件3.3 查看某个用户打开的所有文件3.4 查看某个文件被哪些进程打开3.5 查看…...
性能压力测试的优势与重要性
性能压力测试是软件开发过程中至关重要的一环,它通过模拟系统在极限条件下的运行,以评估系统在正常和异常负载下的表现。这种测试为确保软件系统的可靠性、稳定性和可伸缩性提供了关键信息。下面将探讨性能压力测试的优势以及为什么在软件开发中它具有不…...
AtCoder Beginner Contest 329 题解A~F
A - Spread 输入字符串,字符之间加上空格输出 B - Next 输出数组当中第二大的数 C - Count xxx 统计每个字符出现过的最长长度,再累加即可 #include<bits/stdc.h> #pragma GCC optimize("Ofast") #define INF 0x3f3f3f3f #define I…...
Windows网络「SSL错误问题」及解决方案
文章目录 问题方案 问题 当我们使用了神秘力量加持网络后,可能会和国内的镜像源网站的之间发生冲突,典型的有 Python 从网络中安装包,如执行 pip install pingouin 时,受网络影响导致无法完成安装的情况: pip config…...
python数据可视化
绘制简单的折线图 1.1json数据格式 JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据,其本质上是一个带有特定格式的字符串。 主要功能:json就是一种在各个编程语言中流通的数据格式,负责不同编程语言中的数据传递…...
LV.12 D18 中断处理 学习笔记
一、ARM的异常处理机制及工程代码结构 1.1异常概念 处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生 这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件 异常事件处理完成之后再返回到被异常打断的点继续执行程序。 1.2异常处理机制 不同的处…...
蓝桥杯每日一题2023.11.19
题目描述 “蓝桥杯”练习系统 (lanqiao.cn) 题目分析 首先想到的方法为dfs去寻找每一个数,但发现会有超时 #include<bits/stdc.h> using namespace std; const int N 2e5 10; int n, cnt, a[N]; void dfs(int dep, int sum, int start) {if(dep 4){if(s…...
<b><strong>,<i><em>标签的区别
1. b标签和strong标签 b标签:仅仅是UI层面的加粗样式,并不具备HTML语义 strong标签:不仅是在UI层面的加粗样式,具备HTML语义,表示强调 2. i标签和em标签 i 标签:仅仅是UI层面的斜体样式,并不具备…...
c++中的特殊类设计
文章目录 1.请设计一个类,不能被拷贝2. 请设计一个类,只能在堆上创建对象3. 请设计一个类,只能在栈上创建对象4. 请设计一个类,不能被继承5. 请设计一个类,只能创建一个对象(单例模式) 1.请设计一个类,不能…...
开源更安全? yum源配置/rpm 什么是SSH?
文章目录 1.开放源码有利于系统安全2.yum源配置,这一篇就够了!(包括本地,网络,本地共享yum源)3.rpm包是什么4.SSH是什么意思?有什么功能? 1.开放源码有利于系统安全 开放源码有利于系统安全 2.yum源配置…...
庖丁解牛:NIO核心概念与机制详解 04 _ 分散和聚集
文章目录 Pre概述分散/聚集 I/O分散/聚集的应用聚集写入Code Pre 庖丁解牛:NIO核心概念与机制详解 01 庖丁解牛:NIO核心概念与机制详解 02 _ 缓冲区的细节实现 庖丁解牛:NIO核心概念与机制详解 03 _ 缓冲区分配、包装和分片 概述 分散/聚…...
Java读写Jar
Java提供了读写jar的类库Java.util.jar,Java获取解析jar包的工具类如下: import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.Enumeration; import java.util.HashMap; import …...
【四元数简述】
w cos(theta/2) x ax * sin(theta/2) y ay * sin(theta/2) z az * sin(theta/2) 向量(x,y,z)是旋转轴 a 是任意正数 theta是旋转角度。 上面就是一个四元数表示旋转。 如何使用 空间中向量(1,2,3)扩展为(0,1,2,3&#…...
ClickHouse SQL 查询优化
1 单表查询 1.1 Prewhere替代where Prewhere和where语句的作用相同,用来过滤数据。不同之处在于prewhere只支持 *MergeTree 族系列引擎的表,首先会读取指定的列数据,来判断数据过滤,等待数据过滤之后再读取select 声明的列字段来补…...
「Verilog学习笔记」数据选择器实现逻辑电路
专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点,刷题网站用的是牛客网 分析 将变量A、B接入4选1数据选择器选择输入端S0 S1。将变量C分配在数据输入端。从表中可以看出输出L与变量C的关系。 当AB00时选通D0而此时L0,所以数据端D0接0…...
【Go入门】Web工作方式
【Go入门】 Web工作方式 我们平时浏览网页的时候,会打开浏览器,输入网址后按下回车键,然后就会显示出你想要浏览的内容。在这个看似简单的用户行为背后,到底隐藏了些什么呢? 对于普通的上网过程,系统其实是这样做的&…...
综述:目标检测二十年(机翻版)(未完
原文地址 20年来的目标检测:一项调查 摘要关键词一 介绍二 目标检测二十年A.一个目标检测的路线图1)里程碑:传统探测器Viola Jones探测器HOG检测器基于可变形零件的模型(DPM) 2)里程碑:基于CNN的两阶段探测器RCNNSPPN…...
quinn源码解析:QUIC数据包是如何发送的
quinn源码解析:QUIC数据包是如何发送的 简介QUIC协议中的概念endpoint(端点)connection(连接)Stream(流)Frame (帧) 发包过程解析SendStream::write_allConnectionDriverEndpointDriver 简介 q…...
scss的高级用法——循环
周末愉快呀!一起来学一点简单但非常有用的css小知识。 最近在一个项目中看到以下css class写法: 了解过tailwind css或者unocss的都知道,从命名就可以看出有以下样式: font-size: 30pxmargin-left: 5px;margin-top: 10px; 于是…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
