线程支持库(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…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
