C++ 多线程简要讲解
std::thread是 C++11 标准库中用于多线程编程的核心类,提供线程的创建、管理和同步功能。下面我们一一讲解。
一.构造函数
官网的构造函数如下:
1.默认构造函数和线程创建
thread() noexcept;
作用:创建一个 std::thread 对象,但不关联任何实际线程(即空线程对象)。
如:
std::thread t; // 空线程对象,不执行任何操作
注意:空线程对象不可调用 join() 或 detach(),需先绑定有效线程。
2.初始化构造函数
template <class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args);
- 作用:创建一个新线程,新线程会立即执行
fn(args...)函数。 - 参数:
fn:可调用对象(函数、Lambda、函数对象等)。args:传递给fn的参数
如:
void print(int x) { std::cout << x; }
std::thread t(print, 42); // 线程执行 print(42)
t.join();
这里要注意下面几点
explicit禁止隐式类型转换(如避免误将函数指针转线程对象)。- 参数默认按值拷贝传递,若需传引用需用
std::ref- 示例:
int x = 42;
std::thread t([](int& v) { v++; }, std::ref(x)); // 传递引用
3.拷贝构造函数(被删除)
thread(const thread&) = delete;
- 作用:禁止拷贝构造线程对象(线程资源不可复制)。
- 示例:
-
std::thread t1([]{ /* ... */ }); std::thread t2 = t1; // 编译错误!拷贝构造被禁用 - 原因:线程是独占资源,拷贝可能导致线程重复管理(如多次
join())。
4.移动构造函数
thread(thread&& x) noexcept;
- 作用:将线程所有权从
x转移到新对象(x变为空线程对象)。 - 示例:
std::thread t1([]{ /* ... */ }); std::thread t2 = std::move(t1); // t1 变为空,t2 接管线程 t2.join();
这里要注意下面几点:
- 移动后,原线程对象
x不再关联任何线程。- 用于将线程对象存入容器或转移所有权:std::vector<std::thread> threads; threads.push_back(std::thread([]{ /* ... */ })); // 必须用移动语义
std::vector<std::thread> threads;
threads.push_back(std::thread([]{ /* ... */ })); // 必须用移动语义
二.赋值运算符重载

1. 移动赋值运算符(Move Assignment)
thread& operator=(thread&& rhs) noexcept;
- 关键行为:
- 当前对象会先释放自己原本持有的线程资源(如果存在)。
- 接管
rhs的线程所有权,rhs变为空线程对象(不再关联任何线程)。 - 操作是原子的,且标记为
noexcept(保证不抛出异常)。
- 示例:
std::thread t1([]{ /* 任务 1 */ }); std::thread t2; t2 = std::move(t1); // t1 的线程所有权转移给 t2,t1 变为空 t2.join(); - 典型用途:
- 将线程对象存入容器(如
std::vector<std::thread>)。 - 动态管理线程所有权(如线程池)。
- 将线程对象存入容器(如
2. 拷贝赋值运算符(被删除)
thread& operator=(const thread&) = delete;
- 作用:禁止拷贝赋值(编译时报错)。
- 原因:
- 线程是独占资源,拷贝会导致多个对象管理同一线程,引发重复
join()或detach()。 - 保证线程对象的唯一所有权,避免资源管理冲突。
- 线程是独占资源,拷贝会导致多个对象管理同一线程,引发重复
- 错误示例:
std::thread t1([]{ /* ... */ }); std::thread t2; t2 = t1; // 编译错误!拷贝赋值被禁用
三.线程等待和线程分离
线程等待就是让主线程等待子线程执行完毕,首先我们要明白为什么要进行线程等待。
1.主线程是进程的入口,若主线程执行完毕,操作系统会直接终止整个进程(包括所有子线程),无论子线程是否完成任务。
2.子线程可能持有共享资源(如内存、文件句柄、网络连接),若主线程不等待子线程结束就退出,可能导致:资源泄漏(如未关闭的数据库连接)和数据竞争(子线程访问已被主线程释放的内存,导致崩溃)。
3.部分情况下线程执行计算或数据处理后,主线程需要汇总结果
线程分离就是用于解除线程与其创建者(如主线程)的关联,使得子线程在终止后能够自动释放资源,无需其他线程显式调用 join() 等待或回收。
下面我们就来讲解一下线程等待和线程分离函数join() 和 detach()
join():在主线程中调用阻塞主线程,直到当前线程执行完毕,就是让主线程等待子线程执行完毕
detach():分离线程,使其在后台独立运行(无法再管理)。
必须在 std::thread 对象销毁前调用 join() 或 detach(),否则程序终止。
std::thread t([]{ /* ... */ });if (t.joinable()) {t.join(); // 或 t.detach();
}
四.线程 ID 和命名
-
get_id():获取线程唯一标识符。 -
std::this_thread命名空间提供当前线程操作。有get_id(),yield(),sleep_for()等操作。
std::thread t([]{ std::cout << "Thread ID: " << std::this_thread::get_id() << "\n";
});
std::cout << "Main thread ID: " << t.get_id() << "\n";
t.join();
五.线程中的同步机制
1. 互斥量 std::mutex
防止多个线程同时访问共享资源。
std::mutex mtx;
int counter = 0;void safe_increment() {std::lock_guard<std::mutex> lock(mtx); // 自动加锁解锁counter++;
}std::thread t1(safe_increment);
std::thread t2(safe_increment);
t1.join(); t2.join();
2. 条件变量 std::condition_variable
用于线程间的条件同步。
std::mutex mtx;
std::condition_variable cv;
bool ready = false;void wait_thread() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return ready; }); // 等待条件满足std::cout << "Ready!\n";
}void signal_thread() {std::this_thread::sleep_for(std::chrono::seconds(1));{std::lock_guard<std::mutex> lock(mtx);ready = true;}cv.notify_one(); // 通知等待线程
}std::thread t1(wait_thread);
std::thread t2(signal_thread);
t1.join(); t2.join();
六.实践操作:
用两个线程分别交替打印 1-100 的奇数和偶数,通过互斥锁和条件变量实现交替输出:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>using namespace std;int num = 1; // 共享计数器
mutex mtx; // 互斥锁
condition_variable cv; // 条件变量
const int MAX_NUM = 100; // 最大值void print_odd() {while (true) {unique_lock<mutex> lock(mtx);// 等待条件:数字为奇数或超过最大值cv.wait(lock, [] { return (num % 2 == 1) || (num > MAX_NUM); });if (num > MAX_NUM) break;cout << "Odd: " << num << endl;num++; // 递增到偶数lock.unlock();cv.notify_all(); // 唤醒另一个线程}
}void print_even() {while (true) {unique_lock<mutex> lock(mtx);// 等待条件:数字为偶数或超过最大值cv.wait(lock, [] { return (num % 2 == 0) || (num > MAX_NUM); });if (num > MAX_NUM) break;cout << "Even: " << num << endl;num++; // 递增到奇数lock.unlock();cv.notify_all(); // 唤醒另一个线程}
}int main() {thread t1(print_odd);thread t2(print_even);t1.join();t2.join();return 0;
}
运行结果
c++的线程就讲到这里,有帮助的话就点点赞吧。

相关文章:
C++ 多线程简要讲解
std::thread是 C11 标准库中用于多线程编程的核心类,提供线程的创建、管理和同步功能。下面我们一一讲解。 一.构造函数 官网的构造函数如下: 1.默认构造函数和线程创建 thread() noexcept; 作用:创建一个 std::thread 对象,但…...
如何设计一个处理物联网设备数据流的后端系统。
一、系统架构设计 物联网设备数据流的后端系统通常包括以下几个主要组件: ①设备数据采集层:负责从物联网设备收集数据。 ②数据传输层:负责将设备数据传输到后端系统。 ③数据处理层:实时或批量处理传输到后的数据。 ④存储层:负责存储设备数据。 ⑤API层:提供外部…...
【QT5 多线程示例】信号量
信号量 【C并发编程】(八)信号量 QT中的信号量类是QSemaphore,用法与C标准中的std::counting_semaphore类似。不同的是, QSemaphore无法指定最大计数。为了限定最大计数,可以采用两个QSemaphore信号量。下面使用一个…...
深入理解 Spring Boot 应用的生命周期:从启动到关闭的全流程解析
引言 Spring Boot 是当今 Java 开发中最流行的框架之一,它以简化配置和快速开发著称。然而,要真正掌握 Spring Boot,理解其应用的生命周期是至关重要的。本文将深入探讨 Spring Boot 应用的生命周期,从启动到关闭的各个阶段&…...
【算法笔记】图论基础(一):建图、存图、树和图的遍历、拓扑排序、最小生成树
目录 何为图论图的概念 图的一些基本概念有向图和无向图带权图连通图和非连通图对于无向图对于有向图 度对于无向图对于有向图一些结论 环自环、重边、简单图、完全图自环重边简单图 稀疏图和稠密图子图、生成子图同构 图的存储直接存边邻接矩阵存边邻接表存边链式前向星存边 图…...
SpringMVC 请求与响应处理详解
引言 在 Java Web 开发中,SpringMVC 作为 Spring 框架的重要模块,提供了强大的请求和响应处理机制。本文将深入探讨 SpringMVC 中请求和响应的处理方式,结合实际案例,帮助开发者更好地理解和应用这些功能。 一、SpringMVC 请求处…...
【python】requests 爬虫高效获取游戏皮肤图
1. 引言 在当今的数字时代,游戏已经成为许多人生活中不可或缺的一部分。而游戏中的皮肤,作为玩家个性化表达的重要方式,更是受到了广泛的关注和喜爱。然而,对于许多玩家来说,获取游戏皮肤往往需要花费大量的时间和精力…...
(UI自动化测试web端)第二篇:元素定位的方法_css定位之ID选择器
看代码里的【find_element_by_css_selector( )】( )里的表达式怎么写? 文章介绍了第一种写法id选择器,其实XPath元素定位要比CSS好用,原因是CSS无法使用下标(工作当中也是常用的xpath),但CSS定位速度比XPat…...
23种设计模式-代理(Proxy)设计模式
代理设计模式 🚩什么是代理设计模式?🚩代理设计模式的特点🚩代理设计模式的结构🚩代理设计模式的优缺点🚩代理设计模式的Java实现🚩代码总结🚩总结 🚩什么是代理设计模式…...
【react18】react项目使用mock模拟后台接口
前后端分离项目,后端还没有接口的时候,前端可以使用mockjs的技术实行假数据的模拟。这里使用的是mock的库msw实现这个业务. MSW msw是mock的工具,官网地址是在这里 使用步骤 1.安装msw npm install mswlatest --save-dev2.新建存放mock接…...
Excel新增的函数
常用函数 XLOOKUP 1、普通查找 2、屏蔽错误值 3、横向查找 4、通配符查找 ?:代表任意单个字符 *:代表任意多个字符 5、反向查找 6、多条件查找 7、查找多列数据 8、查找最后一个 IFS MINIFS MAXIFS 文本函数 TEXTSPLIT、TEXTJOIN、CONCAT、 TEXTBEFORE、TEXTAFT…...
Windows下VSCode的安装
前言 VSCode的安装看起来平平无奇,但也不是轻轻松松的。笔者将最新的Windows下安装VSCode,以及运行最简单的C程序的过程记录下来,供后续的自己和大家参考。 一、官网下载安装包 Visual Studio Code - Code Editing. Redefined 二、安装 直接…...
django入门教程之templates和static资源【五】
使用app01子应用举例说明模板的使用。templates官方文档。 templates完整流程认知 第一步,在settings.py中注册app01子应用。 第二步,在app01目录下,新建templates和static目录,用于存放模板文件和资源文件。目录结构如下&#…...
Vue 中directive的钩子函数(bind、inserted 等)的作用及使用场景
大白话Vue 中directive的钩子函数(bind、inserted 等)的作用及使用场景。 在 Vue 里,指令(directive)是个超实用的东西,它能让你在不改动组件逻辑的情况下,给 HTML 元素添加一些特殊的行为。Vu…...
【区块链安全 | 第一篇】密码学原理
文章目录 1.哈希函数1.1 哈希函数的性质1.2 常见哈希算法1.3 Merkle Tree(默克尔树)1.4 HMAC(哈希消息认证码) 2. 公钥密码学2.1 对称加密 vs 非对称加密2.2 RSA 算法2.3 ECC(椭圆曲线密码学)2.4 Diffie-He…...
Linux安装MySQL数据库并使用C语言进行数据库开发
目录 一、前言 二、安装VMware运行Ubuntu 1.安装VMware 2.使用VMware打开Ubuntu 三、配置VMware使用网卡 1.添加NAT网卡 四、Linux下安装MySQL数据库 五、安装MySQL开发库 六、演示代码 sql_connect.c sql_connect.h main.c中数据库相关代码 结尾 一、前言 由于最…...
2024年MathorCup数学建模A题移动通信网络中PCI规划问题解题全过程文档加程序
2024年第十四届MathorCup高校数学建模挑战赛 A题 移动通信网络中PCI规划问题 原题再现: 物理小区识别码(PCI)规划是移动通信网络中下行链路层上,对各覆盖小区编号进行合理配置,以避免PCI冲突、PCI混淆以及PCI模3干扰等现象。PCI规划对于减少…...
伯努利分布和二项分布学习笔记
目录 1. 伯努利分布1.1定义1.2概率质量函数1.3数学期望与方差1.4应用示例 2. 二项分布2.1定义2.1概率质量函数2.2数学期望与方差2.3性质与图形 3. 伯努利分布与二项分布的关系4. 总结 1. 伯努利分布 伯努利分布(Bernoulli Distribution),又称…...
Redis实战常用二、缓存的使用
一、什么是缓存 在实际开发中,系统需要"避震器",防止过高的数据访问猛冲系统,导致其操作线程无法及时处理信息而瘫痪. 这在实际开发中对企业讲,对产品口碑,用户评价都是致命的。所以企业非常重视缓存技术; 缓存(Cache):就是数据交换的缓冲区&…...
G口服务器和普通服务器之间的区别
今天小编主要来为大家介绍一下G口服务器和普通服务器之间的区别! 首先,从硬件配置上看,普通服务器通常都会配备中央处理器、内存和硬盘等基本的硬件配置,能够适用于各种应用程序和服务;G口服务器除了基础的硬件配置还增…...
通过国内源在Ubuntu20.0.4安装repo
国内三大免费源: 清华大学:清华大学开源软件镜像站 | Tsinghua Open Source Mirror中国科技大学:USTC Open Source Software Mirror阿里云:阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 repo只在清华源网站里搜到:…...
多维动态规划 力扣hot100热门面试算法题 面试基础 核心思路 背题
多维动态规划 不同路径 https://leetcode.cn/problems/unique-paths/ 核心思路 比较简单 f[i][j] f[i - 1][j] f[i][j - 1] ; 示例代码 class Solution {public int uniquePaths(int n, int m) {int[][] f new int[n][m];for (int i 0; i < n; i)f[i][0] 1;for…...
《Java到Go的平滑转型指南》
文章目录 **文章摘要****核心主题****关键内容提炼****决策者行动清单****核心结论** **第一章:转型决策:为什么要从Java转向Go?****1.1 性能对比:GC机制与并发模型差异****GC机制对比****并发模型基准测试** **1.2 开发效率&…...
【软件测试】:软件测试实战
1. ⾃动化实施步骤 1.1 编写web测试⽤例 1.2 ⾃动化测试脚本开发 common public class AutotestUtils {public static EdgeDriver driver;// 创建驱动对象public static EdgeDriver createDriver(){// 驱动对象已经创建好了 / 没有创建if( driver null){driver new EdgeDr…...
SpringMVC 请求处理
SpringMVC 请求处理深度解析:从原理到企业级应用实践 一、架构演进与核心组件协同 1.1 从传统Servlet到前端控制器模式 SpringMVC采用前端控制器架构模式,通过DispatcherServlet统一处理请求,相比传统Servlet的分散处理方式,实…...
unittest自动化测试实战
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 为什么要学习unittest 按照测试阶段来划分,可以将测试分为单元测试、集成测试、系统测试和验收测试。单元测试是指对软件中的最小可测试单元在与程…...
leetcode3.无重复字符的最长字串
采用滑动窗口方法 class Solution { public:int lengthOfLongestSubstring(string s) {int ns.size();if(n0)return 0;int result0;unordered_set<char> set;set.insert(s[0]);for(int i0,j0;i<n;i){while(j1<n&&set.find(s[j1])set.end()){set.insert(s[…...
Android Compose 框架派生状态(derivedStateOf、rememberCoroutineScope)深入剖析(十五)
一、引言 在 Android 开发领域,高效的状态管理对于构建响应式、高性能的应用程序至关重要,在 Jetpack Compose 中,derivedStateOf 和 rememberCoroutineScope 这两个与派生状态相关的特性在状态管理方面发挥着关键作用。派生状态允许我们根据…...
3.25-2request库
request库 一、介绍request库 (1)requests是用python语言编写的简单易用的http库,用来做接口测试的库; (2)接口测试自动化库有哪些? requests、urllib 、urllib2、urllib3、 httplib 等&…...
《破解老龄化的智能密钥:机器人四维战略与未来养老生态》
一、引言:老龄化社会与智能机器人的必然性 全球老龄化趋势与老年人核心需求(健康管理、生活辅助、心理陪伴、安全保障) 全球正面临着严峻的老龄化挑战。根据联合国发布的数据,全球60岁及以上人口数量在过去几十年中持续增长&…...
