线程支持库(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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/458360.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Rust】环境搭建

▒ 目录 ▒ &#x1f6eb; 导读需求 1️⃣ 安装Chocolatey安装依赖 2️⃣ 安装RustRover安装toolchain&#xff08;rustup、VS&#xff09;重启配置生效设置安装插件 &#x1f4d6; 参考资料 &#x1f6eb; 导读 需求 重装系统&#xff0c;记录下环境搭建遇到的问题。 1️⃣ …

【最全基础知识2】机器视觉系统硬件组成之工业相机镜头篇--51camera

机器视觉系统中,工业镜头作为必备的器件之一,须和工业相机搭配。工业镜头是机器视觉系统中不可或缺的重要组成部分,其质量和性能直接影响到整个系统的成像质量和检测精度。 目录 一、基本功能和作用 二、分类 1、按成像方式分 2、按焦距分 3、按接口类型分 4、按应用…

如何制定有效的学习计划

文章目录 第一章&#xff1a;目标设定1.1 目标的重要性1.2 SMART原则1.3 目标设定公式 第二章&#xff1a;时间管理2.1 时间的重要性2.2 制定时间表2.3 时间管理公式2.4 番茄工作法2.5 时间分配公式 第三章&#xff1a;学习策略3.1 学习方法3.2 学习材料的选择3.3 学习效果公式…

量子计算突破:下一个科技革命的风口浪尖在哪里?

内容概要 在当今科技飞速发展的时代&#xff0c;量子计算如同一颗璀璨的明珠&#xff0c;正闪烁着无尽的可能性。它不仅是解决科学难题的钥匙&#xff0c;更是即将引领科技革命的先锋。如今&#xff0c;随着技术的不断突破&#xff0c;量子计算已经步入了一个崭新的阶段。想象…

【ZZULI】数据库第二次实验

【ZZULI】数据库第二次实验 创建学生信息管理系统的数据库通过T-SQL语句创建学生表、课程表、选课表创建学生表创建课程表创建选课表 修改表结构。为SC表添加写的列&#xff0c;列名为备注修改备注列的数据长度。删除SC表的备注列。 通过T-SQL语句对表的列添加约束&#xff0c;…

iOS静态库(.a)及资源文件的生成与使用详解(OC版本)

引言 iOS静态库&#xff08;.a&#xff09;及资源文件的生成与使用详解&#xff08;Swift版本&#xff09;_xcode 合并 .a文件-CSDN博客 在前面的博客中我们已经介绍了关于iOS静态库的生成步骤以及关于资源文件的处理&#xff0c;在本篇博客中我们将会以Objective-C为基础语言…

实验:使用Oxygen发布大型手册到Word格式

此前&#xff0c;我曾发表过一篇文章《结构化文档发布的故事和性能调优》&#xff0c;文中讨论了在将大型DITA手册转换为PDF格式时可能遇到的性能挑战及相应的优化策略。 近日&#xff0c;有朋友咨询&#xff0c;若将同样的大型手册输出为MS Word格式&#xff0c;是否也会面临…

从0到1,用Rust轻松制作电子书

我之前简单提到过用 Rust 做电子书&#xff0c;今天分享下如何用Rust做电子书。制作电子书其实用途广泛&#xff0c;不仅可以用于技术文档&#xff08;对技术人来说非常方便&#xff09;&#xff0c;也可以制作用户手册、笔记、教程等&#xff0c;还可以应用于文学创作。 如果…

Python应用指南:利用高德地图API实现路径规划

高德路径规划API是一套基于HTTP协议的接口服务&#xff0c;旨在为开发者提供便捷的路径规划解决方案。该API支持多种出行方式&#xff0c;包括步行、公交和驾车&#xff0c;能够满足不同场景下的路径查询需求。通过调用这些API&#xff0c;用户可以获得从起点到终点的最优路径建…

【Web.路由】——路由原理

这篇文章&#xff0c;我们来讲一讲什么是路由。 路由是 将用户请求地址映射为一个请求委托的过程&#xff0c;负责匹配传入的Http请求&#xff0c;然后将这些请求发送到应用的可执行终结点。 这里需要注意一个内容&#xff0c;发送到应用的可执行终结点。 路由的分类&#x…

大模型落地,要追求极致的务实主义

图源&#xff1a;AI生成 ▎****更快用上最新的大模型&#xff0c;是不是就赢定了&#xff1f; “能够像人类一样操作电脑。”这一堪称革命性的新技能来自10月23日Anthropic最新推出的升级版Claude 3.5 Sonnet模型&#xff0c;据介绍该模型不仅各项性能指标上取得显著提升&…

MobileNetV2实现实时口罩检测tensorflow

项目源码获取方式见文章末尾&#xff01; 回复暗号&#xff1a;13&#xff0c;免费获取600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 **《------往期经典推荐------》**项目名称 1.【Informer模型复现项目实战】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【…

机器学习 - 概述、数据集、Scikit-learn

目录 一、人工智能概述1、概念2、应用领域 二、机器学习1、概念2、数据集的构成3、算法分类a、监督学习b、无监督学习 4、开发流程5、可用数据集总结 三、Scikit-learn1、介绍2、安装3、数据集API介绍4、数据集返回值介绍5、数据集的划分6、数据集划分的API介绍7、案例a、获取鸢…

sass软件数据架构思路——未来之窗行业应用跨平台架构

一、SAAS多商户数据库 1.1 SaaS 多商户数据库的全部商户数据放在一个服务器上 1. 成本效益&#xff1a;集中管理和维护一个服务器通常比维护多个独立服务器更经济&#xff0c;降低硬件、运维和管理成本。 2. 数据集中管理&#xff1a;便于进行统一的数据备份、恢复和数据治理…

高清 MV 无字幕视频素材

在当下的短视频和自媒体时代&#xff0c;高清无字幕的视频素材无疑是创作者们的“得力助手”。不管是用于剪辑情感励志视频、制作搞笑段子&#xff0c;还是创作风景航拍视频&#xff0c;优质的素材库都能让你的创作如虎添翼。今天&#xff0c;我就为大家介绍几个海外的高质量素…

光学计算领域的突破:更快、更高效的光子存储单元

国际电气工程师团队首次开发出一种新的光子内存计算方法&#xff0c;这将在不久的将来使光学计算成为现实。 该团队包括来自匹兹堡大学斯旺森工程学院、加州大学圣巴巴拉分校、卡利亚里大学和东京工业大学&#xff08;现东京科学大学&#xff09;的研究人员。他们的研究成果发…

【论文阅读】Reliable, Adaptable, and Attributable Language Models with Retrieval

文章目录 OverviewCurrent Retrieval-Augmented LMsArchitectureTraining Limitations & Future Work Overview Parametic language models的缺点&#xff1a; 事实性错误的普遍存在验证的难度&#xff08;可溯源性差&#xff09;难以在有顾虑的情况下排除某些序列适应调整…

web pdf 图片拖动图片合成

web pdf 图片拖动图片合成 先看效果 前端 合成后 1.原理 以前写过相关的帖子&#xff0c;使用的是 canva 但是这次换了一个思路使用的是图片 1.先把pdf转成图片 2.把pdf图片和目标图片传到浏览器 3.原理就和图片合成一样了。见上一篇帖子 4.后端也一样只不过这次是将位置和pd…

LeetCode 107.二叉树的层次遍历 II

题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&#xff09; 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[1…

【力扣专题栏】两数相加,如何实现存储在链表中的整数相加?

题解目录 1、题目描述解释2、算法原理解析3、代码编写&#xff08;原始版本&#xff09;4、代码编写&#xff08;优化版本&#xff09; 1、题目描述解释 2、算法原理解析 3、代码编写&#xff08;原始版本&#xff09; /*** Definition for singly-linked list.* struct ListN…