线程支持库(C++11)
线程支持库包含了线程,互斥锁,线程条件变量(class thread),定义于<thread>
线程提供一个并发的实例,需要对应一个“线程函数”
线程的主要任务就是去执行这个"线程函数"
既然线程需要提供一个线程函数,在线程实例化的时候,需要一个"线程函数"构造一个线程类的对象,需要提供一个参数(线程函数)
C++的线程类就是包装了POSIX标准的线程接口,所以在编译的时候需要链接多线程库
-lpthread
最基本的用法:thread t{线程函数,线程函数的参数......};// 实例化一个线程对象,创建成功之后,就会自动运行
t.join();// 等待线程退出
01 线程类的用法(创建一个线程,让新创建的线程完成指定的功能)
(1) 线程函数是全局函数
#include <iostream> #include <thread>using namespace std;// 全局的线程函数(按值传递) void print_arg(int a) {int c = 10;while (c--) {cout << "a:" << a++ <<endl;// 线程休眠一段时间this_thread::sleep_for(1s);} }// 全局的线程函数(按引用传递) void print_arg1(int & a) { // a成为你实际传入的参数的别名int c = 10;while (c--) {cout << "a:" << a++ <<endl;//线程休眠一段时间this_thread::sleep_for(1s);} }int main() {// 实例化一个线程对象,自动开始运行// thread t{线程函数, 线程函数的参数};int n = 10;// thread t{print_arg, n}; // 创建一个线程,按值传递参数thread t{print_arg1, std::ref(n)}; // 创建一个线程,按引用传递参数// 传参的时候,指定n以引用的方式传递给线程的构造函数/*thread(线程函数地址, int x = n) { // 线程的构造函数pthread_create(...线程函数地址,&x);}*/// 完成其他的任务int c = 10;while (c--) {cout << "hello" <<endl;// 线程休眠一段时间this_thread::sleep_for(1s);}// 等待线程结束t.join(); // 阻塞cout << "n:" << n << endl;return 0; }
(2) 线程函数是类的static函数
类的static函数
a. 可以通过类名调用
b. 没有this指针
c. 只能访问类的静态成员(不能访问类的非static成员)
使用方式:
thread t{类名::静态成员函数名,线程函数的参数};
// 线程函数是类的static函数#include <iostream> #include <thread>using namespace std;class A {public:// 线程函数(按值传递)static void print_arg(int a) {int c = 10;while (c--) {cout << "a:" << a++ <<endl;// 线程休眠一段时间this_thread::sleep_for(1s);}}// 线程函数(按引用传递)static void print_arg1(int & a) { // a成为你实际传入的参数的别名int c = 10;while (c--) {cout << "a:" << a++ <<endl;// 线程休眠一段时间this_thread::sleep_for(1s);}} };int main() {// 实例化一个线程对象,自动开始运行// thread t{线程函数, 线程函数的参数};int n = 10;// A::print_arg()// thread t{A::print_arg, n}; // 创建一个线程,按值传递参数thread t{A::print_arg1, std::ref(n)}; // 创建一个线程,按引用传递参数// 传参的时候,指定n以引用的方式传递给线程的构造函数// 完成其他的任务int c = 10;while (c--) {cout << "hello" <<endl;// 线程休眠一段时间this_thread::sleep_for(1s);}// 等待线程结束t.join(); // 阻塞cout << "n:" << n << endl;return 0; }
(3) 线程函数是类的普通成员函数
普通的全局函数和静态函数都可以直接调用,不需要通过对象,但是类的普通成员函数不能直接调用,必须通过对象调用(因为有一个隐式的参数:this)
所以,当一个类的非静态成员函数作为线程函数时,我们需要传递一个该类的对象的地址作为this指针的实参
语法:
thread t{&类名::成员函数名,该类对象的地址,线程函数的参数};
// 线程函数是类的非static函数#include <iostream> #include <thread>using namespace std;class A {public:// 线程函数(按值传递)void print_arg(int a) {int c = 10;while (c--) {cout << "a:" << a++ <<endl;// 线程休眠一段时间this_thread::sleep_for(1s);}}// 线程函数(按引用传递)void print_arg1(int & a) { // a成为你实际传入的参数的别名int c = 10;while (c--) {cout << "a:" << a++ <<endl;// 线程休眠一段时间this_thread::sleep_for(1s);}} };int main() {// 实例化一个线程对象,自动开始运行// thread t{线程函数, 线程函数的参数};int n = 10;A a; // 实例化一个对象// a.print_arg(n) -----> print_arg(&a, n)// thread t{&A::print_arg, &a, n}; // 创建一个线程,按值传递参数thread t{&A::print_arg1, &a, std::ref(n)}; // 创建一个线程,按引用传递参数// 传参的时候,指定n以引用的方式传递给线程的构造函数// 完成其他的任务int c = 10;while (c--) {cout << "hello" <<endl;// 线程休眠一段时间this_thread::sleep_for(1s);}// 等待线程结束t.join(); // 阻塞cout << "n:" << n << endl;return 0; }
以上三种就是线程最普通的用法
02 线程互斥锁(mutex)
线程互斥锁是用来避免多个并发实例对共享资源的访问产生竞争,定义于头文件<mutex>
class mutex;
class timed_mutex;
std::mutex 既不可以复制,也不可以移动(删除了operator=,也没有实现移动构造函数)
默认构造函数可以初始化一个锁对象,默认是处于解锁状态的
函数:lock 上锁,阻塞到获取锁
try_lock 尝试获取锁,获取失败则返回false
unlock 解锁
用法:mutex m;// 实例化一个锁对象
m.lock(); // 访问共享资源前上锁 m.try_lock();
......; // 访问共享资源的代码(临界区)
m.unlock(); // 访问完共享资源后解锁
#include <iostream> #include <thread> #include <mutex>using namespace std;mutex m; // 全局的锁// 全局的共享资源 int x = 0;// 是一个不可重入的函数 void add_x() {int c = 1000000;while (c--) {// while (!m.try_lock()); // 上锁m.lock();x++; // 共享资源m.unlock(); } }int main() {// 实例化一个线程对象,自动开始运行thread t1{add_x}; thread t2{add_x}; // 等待线程结束t1.join(); // 阻塞t2.join(); // 阻塞cout << "x:" << x << endl;return 0; }
mutex,timed_mutex ...... 可以单独使用,但是可能会遇到一些问题
如:(1) 程序员不注意,造成了"带锁退出" ------> deadlock
xxx() {
m.lock();
......
if (...) {
return ;
}
m.unlock();
}
(2) 同时获取多个锁,推进的顺序不一样
xxx() {
m1.lock();
m2.lock();
......
......
m2.unlock();
m1.unlock();
}
yyy() {
m2.lock();
m1.lock();
......
......
m1.unlock();
m2.unlock();
}
基于这样的原因,C++标准库中,提供了一些互斥锁的包裹类
常用的是:
std::lock_guard<std::mutex> guard{m};
std::unique_lock<std::mutex> lock{m};
可以自动的管理指定的锁,在代码作用域结束后,可以自动的释放指定的锁,可以防止带锁退出
如:
mutex m; // 普通的互斥锁,默认是处于解锁状态的
{ // 描述的是一个作用域范围
std::lock_guard<std::mutex> guard{m}; // 使用一个管理对象guard管理锁m
// ...... 临界区代码
return ;
}
上面的代码是使用lock_guard类型去管理m这个锁,当构造guard的时候自动的给m表示的锁上锁,当guard超出作用域范围后,guard管理的锁自动的解锁
#include <iostream> #include <thread> #include <mutex>using namespace std;mutex m; // 全局的锁// 全局的共享资源 int x = 0;// 是一个不可重入的函数 void add_x() {int c = 1000000;while (c--) {lock_guard<mutex> g{m}; // 使用包裹类对象g管理m表示的锁x++; // 共享资源} }int main() {// 实例化一个线程对象,自动开始运行thread t1{add_x}; thread t2{add_x}; // 等待线程结束t1.join(); // 阻塞t2.join(); // 阻塞cout << "x:" << x << endl;return 0; }
unique_lock是lock_guard的升级版,提供了额外的接口
也能够在超出作用域范围之后,自动的释放管理的锁
用于指定锁定策略的标签常量(常量):
defer_lock
try_to_lock
adopt_lock
但是还有一些额外的功能:
如:
1. 同时获取多个锁
mutex m1,m2;
{
// 希望同时获取m1和m2,要么都获取成功,要么都不成功
// 创建之后,实际上没有获取锁
std::unique_lock<std::mutex> lock1{m1, std::defer_lock};
std::unique_lock<std::mutex> lock2{m2, std::defer_lock};
// 同时获取lock1和lock2表示的两个锁
std::lock(lock1, lock2); // 同时获取
// ......临界区代码
}
2. 管理锁住的粒度(操作共享资源的代码块的大小)
void add_x1()
{
int c = 100000;
// explicit unique_lock( mutex_type & m);
// 没加参数表示构造的时候获取锁
std::unique_lock<std::mutex> lock{m}; // 构造的时候上锁
while (c--)
{
x++;
lock.unlock(); // 手动释放锁
scanf("......");
lock.lock(); // 手动加锁
}
}
03 线程条件变量
线程可以等待一个程序员人为设置的一个"条件"
条件不满足的时候,线程可以等待这个条件,当条件满足的时候,线程可以继续往下运行定义于头文件 <condition_variable>
class condition_variable
能够阻塞一个线程,或者同时阻塞多个线程,直到条件变量表示的”条件“成立,并且被其他地方通知
======>用于”生产者-消费者“模式
#include <iostream> #include <thread> #include <mutex> #include <condition_variable>using namespace std;mutex m; // 全局的锁 condition_variable cond; // 条件变量,表示程序员抽象出来的一个条件// 全局的共享资源 int x = 0;// 生产者函数 void add_x() {while (1) {// while (!m.try_lock()); // 上锁m.lock();x++; // 生产数据cout << "生产者:" << x << endl;if (x > 5) { // 表示满足消费条件,应该通知消费者消费数据cout << "通知消费者!" << endl;cond.notify_all(); // 通知所有等待的线程}m.unlock();this_thread::sleep_for(1s); } }// 生产者函数 // void add_x() { // while (1) { // // while (!m.try_lock()); // 上锁 // m.lock(); // x++; // 生产数据 // cout << "生产者:" << x << endl; // m.unlock(); // this_thread::sleep_for(1s); // } // }// 消费者函数 void delete_x() {while (1) {// 没加参数表示构造的时候获取锁unique_lock<mutex> lk{m}; // 构造的时候上锁if (x > 5) {x = x - 5; // 消费数据cout << "消费者:" << x << endl;} else {cout << "我在浪费CPU!" << endl;cout << "等待一个条件" << endl;cond.wait(lk); // 原子的解锁lock,阻塞等待cond表示的条件}} }// 消费者函数 // void delete_x() { // while (1) { // // while (!m.try_lock()); // m.lock(); // if (x >= 5) { // x = x - 5; // 消费数据 // cout << "消费者:" << x << endl; // } else { // cout << "我在浪费CPU!" << endl; // } // m.unlock(); // this_thread::sleep_for(1s); // } // }int main() {// 实例化一个线程对象,自动开始运行thread t1{add_x}; thread t2{delete_x}; // 等待线程结束t1.join(); // 阻塞t2.join(); // 阻塞return 0; }
相关文章:
线程支持库(C++11)
线程支持库包含了线程,互斥锁,线程条件变量(class thread),定义于<thread> 线程提供一个并发的实例,需要对应一个“线程函数” 线程的主要任务就是去执行这个"线程函数" 既然线程需要提供一个线程函数,…...

【计网】深入理解NAT机制,内网穿透与内网打洞,代理服务
我没胆量犯错 才把一切错过 --- 林夕 《我对不起我》--- 一文了解NAT机制,代理服务,内网穿透 1 再谈 NAT 机制2 内网穿透与内网打洞3 代理服务器 1 再谈 NAT 机制 NAT机制我们在解决IP地址不足的问题中提到过。为了解决IP地址不足的问题,采…...

C# 创建型设计模式----工厂模式
1 、什么是工厂模式 简单来说就是由一个对象去生成不同的对象,工厂模式是用工厂方法代替new操作的一种模式。工厂方法封装了多个相关联类的new方法,每次实例化这些类的时候不需要new多次,只需要调用工厂类的对应方法即可实例化这些类&#x…...

java中Scanner的nextLine和next方法
思考,输入1 2 3 4 5加上enter,输出什么 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int[][] m new int[2][2];for (int i 0; i < 2; i) {for (int j 0; j < 2;…...
2024年全国山洪径流模拟与洪水危险性评价技术及典型地区洪水淹没及损失分析
洪水淹没危险性(各种年遇型洪水淹没)是洪水损失评估、风险评估及洪水应急和管理规划等工作的重要基础。当前开展洪水危险性研究工作中的主要困难之一是水文资料稀缺,尤其是径流资料稀缺,既包括径流观测资料在时间上的短缺…...
CDC 同步数据需要的MySQL数据权限
授权命令如下: grant Replication client on *.* to username%; grant Replication slave on *.* to username%; flush privileges;...
Ubuntu20.04 更新Nvidia驱动 + 安装CUDA12.1 + cudnn8.9.7
一、概述 最近客户给了几台GPU服务器,长期放置落灰那种,然后想利用起来,所以上去看看了配置,系统是Ubuntu20.04,相关的驱动版本稍嫌老一些,所以需要更新Nvidia驱动,同时在安装CUDA和CUDNN,查看了显卡型号之后,打算使用onnxruntime进行推理,对比了版本,最后选择了CUD…...
算法自学 Lesson3 - 逻辑回归(LR)
目录 背景 一、适用数据集 1. 数据集选择 1.1 领域 1.2 数据集维度 1.3 记录行(样本数量) 2. 本文数据集介绍 3. 数据集下载 注意 二、逻辑回归的基本原理 1. 目的 2. Sigmoid 函数 3. 类别划分 4. 召回率 三、代码 1. 导入所需包&数…...

文件IO流
1.文件流概念 2.文件创建方式 3.常用方法 4.IO流原理 (1)InputStream,OutputStream, Reader, Writer四个都是抽象类,无法直接new, 需要由子类继承,然后new子类; (2)Reader和Writer…...

拥塞控制与TCP子问题(粘包问题,异常情况等)
拥塞控制 除了拥塞控制 以上的策越都是为了解决tcp 客户端和服务端提高效率,解决丢包的策略 但是拥塞控制是了为解决网络拥堵 出现了大面积丢包,我们发送方会判定是网络出现了问题? 丢包好解决,我们直接采用超时重传&#…...

stm32入门教程--DMA 超详细!!!
目录 简介 工作模式 1、数据转运DMA 2、ADC扫描模式DMA 简介 工作模式 1、数据转运DMA 这个例子的任务是将SRAM的数组DataA,转运到另一个数组DataB中,这个基本结构里的各个参数应该如何配置呢? 首先是外设站点和存储器站点的起始地址、…...
【使用Flask构建RESTful API】从零开始开发简单的Web服务!
使用Flask构建RESTful API:从零开始开发简单的Web服务 引言 随着Web应用程序的广泛使用,RESTful API已成为现代Web服务的核心技术之一。通过RESTful API,我们可以轻松地创建、读取、更新和删除(CRUD)数据,…...

用sdcc给51单片机编译C程序
学习单片机大部分人用的是Keil uVision,虽然好用,可大部分人用的是盗版,其实单片机程序小的话,完全可以用文本编辑器(推荐notepad)编写,然后用免费的sdcc来编译,下面介绍一下大致的过程。 sdcc…...
Java Lock LockSupport 源码
前言 相关系列 《Java & Lock & 目录》(持续更新)《Java & Lock & LockSupport & 源码》(学习过程/多有漏误/仅作参考/不再更新)《Java & Lock & LockSupport & 总结》(学习总结/最新…...
Elasticsearch基础操作入门
阅前准备知识 学习 Elasticsearch (简称 ES) 的查询方式,建议从以下几个步骤入手: 理解 Elasticsearch 的基础概念 首先要了解 Elasticsearch 的核心概念,例如: Index(索引):相当于数据库中…...
跨域问题解决办法
跨域问题在Web开发中是一个常见的问题,特别是在前后端分离的开发模式下。以下是一些解决跨域问题的办法: 一、后端配置CORS(跨来源资源共享) CORS是一种机制,它使用额外的HTTP头来告诉浏览器一个网页的当前来源&…...
【数据结构与算法】力扣 23. 合并 K 个升序链表
题干描述 23. 合并 K 个升序链表 给你一个链表数组,每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中,返回合并后的链表。 示例 1: 输入: lists [[1,4,5],[1,3,4],[2,6]] 输出: [1,1,2,3,4,4,5,6]…...

Java Lock CountDownLatch 总结
前言 相关系列 《Java & Lock & 目录》(持续更新)《Java & Lock & CountDownLatch & 源码》(学习过程/多有漏误/仅作参考/不再更新)《Java & Lock & CountDownLatch & 总结》(学习总…...

vue+spreadjs开发
创建vue3项目 pnpm create vite --registryhttp://registry.npm.taobao.org安装spreadjs包 pnpm install "grapecity-software/spread-sheets17.1.7" "grapecity-software/spread-sheets-resources-zh17.1.7" "grapecity-software/spread-sheets-vu…...
针对初学者的PyTorch项目推荐
文章目录 1. MNIST手写数字识别2. CIFAR-10图像分类3. 图像风格迁移4. 文本生成(使用RNN)5. 简单的问答系统6. 简单的生成对抗网络(GAN)7. 简单的推荐系统 对于初学者来说,选择一些简单且具有教育意义的项目来实践PyTo…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...