C++ 线程库(thread)与锁(mutex)

news/2024/5/8 22:32:00/文章来源:https://blog.csdn.net/Emilia_is_angel/article/details/137092410

一.线程库(thread)

1.1 线程类的简单介绍

thread类文档介绍

在C++11之前,涉及到多线程问题,都是和平台相关的,比如windows和linux下各有自己的接口,这使得代码的可移植性比较差。C++11中最重要的特性就是对线程进行支持了,使得C++在并行编程时不需要依赖第三方库,而且在原子操作中还引入了原子类的概念。要使用标准库中的线程,必须包含< thread >头文件。

函数名功能
thread()构造一个线程对象,没有关联任何线程函数,即没有启动任何线程thread(fn, args, …)构造一个线程对象,并关联线程函数fn,args,…为线程函数的参数
get_id()获取线程id
joinable()线程是否还在执行,joinable代表的是一个正在执行中的线程
join()该函数调用后会阻塞住线程,当该线程结束后,主线程继续执行
detach()在创建线程对象后马上调用,用于把被创建线程与线程对象分离开,分离的线程变为后台线程,创建的线程的"死活"就与主线程无关

注意:

  1. 线程是操作系统中的一个概念,线程对象可以关联一个线程,用来控制线程以及获取线程的状态。
  2. 当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程。
  3. 当创建一个线程对象后,并且给线程关联线程函数,该线程就被启动,与主线程一起运行。
  4. thread类是防拷贝的,不允许拷贝构造以及赋值,但是可以移动构造和移动赋值。
  5. 可以通过joinable()函数判断线程是否是有效的。

get_id的返回值是什么?

#include <thread>
int main()
{std::thread t1;cout << t1.get_id() << endl;return 0;
}

get_id()的返回值类型为id类型,id类型实际为std::thread命名空间下封装的一个类,该类中包含了一个结构体:

// vs下查看
typedef struct
{ /* thread identifier for Win32 */void *_Hnd; /* Win32 HANDLE */unsigned int _Id;
} _Thrd_imp_t;

如何给线程对象关联线程函数?

  • 函数指针
  • 函数对象(仿函数)
  • lambda表达式
  • 包装器
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
void ThreadFunc(int a)
{cout << "Thread1" << a << endl;
}
class TF
{
public:void operator()(){cout << "Thread3" << endl;}
};
int main()
{// 线程函数为函数指针thread t1(ThreadFunc, 10);// 线程函数为lambda表达式thread t2([] {cout << "Thread2" << endl; });// 线程函数为函数对象(仿函数)thread t3(*ThreadFunc, 10);TF tf;thread t4(tf);// 线程函数为包装器function<void()> func = []() { cout << "Thread5" << endl; };thread t5(func);t1.join();t2.join();t3.join();t4.join();t5.join();cout << "Main thread!" << endl;return 0;
}

1.2 线程函数参数

线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的的,因此:即使线程参数为引用类型,在线程中修改后也不能修改外部实参,因为其实际引用的是线程栈中的拷贝,而不是外部实参。

#include <thread>
void ThreadFunc1(int& x)
{x += 10;
}
void ThreadFunc2(int* x)
{*x += 10;
}
int main()
{int a = 10;// 在线程函数中对a修改,不会影响外部实参,因为:线程函数参数虽然是引用方式,但其实际引用的是线程栈中的拷贝thread t1(ThreadFunc1, a);t1.join();cout << a << endl;// 如果想要通过形参改变外部实参时,必须借助std::ref()函数thread t2(ThreadFunc1, std::ref(a);t2.join();cout << a << endl;// 地址的拷贝thread t3(ThreadFunc2, &a);t3.join();cout << a << endl;return 0;
}

二.锁

2.1 锁的介绍

随着计算机硬件的发展,多核CPU变得越来越普遍,多线程编程成为提升系统性能的重要手段。然而,多线程并发访问共享资源可能导致数据不一致性和竞态条件等问题。因此,C++11为了标准化并发编程模型,引入了基于内存模型的一系列并发支持,其中就包含了锁这样的同步原语。

2.1 mutex的种类

  1. std::mutex

互斥访问:互斥锁(std::mutex)是最基础的同步对象,它可以阻止多个线程同时进入临界区(一段需要互斥执行的代码)。当一个线程获取了互斥锁后,其他试图获取该锁的线程会阻塞,直到该锁被释放。

函数名函数功能
lock()上锁:锁住互斥量
unlock()解锁:释放对互斥量的所有权
try_lock()尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞
  1. std::recursive_mutex

递归锁定:std::recursive_mutex 允许同一线程多次获得同一个锁,这是针对那些可能会递归调用并需要重新锁定相同资源的情况。

  1. std::timed_mutex

定时锁:std::timed_mutex 类似于普通互斥锁,但增加了尝试锁定指定时间段的功能,超时后不会阻塞而是返回错误。

函数名函数功能
try_lock_for()接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住(与std::mutex 的 try_lock() 不同,try_lock 如果被调用时没有获得锁则直接返回false),如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。
try_lock_until()接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。

2.2 lock_guard

std::lock_gurad 是 C++11 中定义的模板类。定义如下:

template<class _Mutex>
class lock_guard
{
public:// 在构造lock_gard时,_Mtx还没有被上锁explicit lock_guard(_Mutex& _Mtx): _MyMutex(_Mtx){_MyMutex.lock();}// 在构造lock_gard时,_Mtx已经被上锁,此处不需要再上锁lock_guard(_Mutex& _Mtx, adopt_lock_t): _MyMutex(_Mtx){}~lock_guard() _NOEXCEPT{_MyMutex.unlock();}lock_guard(const lock_guard&) = delete;lock_guard& operator=(const lock_guard&) = delete;
private:_Mutex& _MyMutex;
};

lock_guard类模板主要是通过RAII的方式,对其管理的互斥量进行了封装,在需要加锁的地方,只需要用上述介绍的任意互斥体实例化一个lock_guard,调用构造函数成功上锁,出作用域前,lock_guard对象要被销毁,调用析构函数自动解锁,可以有效避免死锁问题。但是其使用方法太单一,用户没有办法对该锁进行控制,因此C++11又提供了unique_lock。

2.3 unique_lock

与lock_guard类似,unique_lock类模板也是采用RAII的方式对锁进行了封装,并且也是以独占所有权的方式管理mutex对象的上锁和解锁操作,即其对象之间不能发生拷贝。在构造(或移动(move)赋值)时,unique_lock 对象需要传递一个 Mutex 对象作为它的参数,新创建的unique_lock 对象负责传入的 Mutex 对象的上锁和解锁操作。使用以上类型互斥量实例化unique_lock的对象时,自动调用构造函数上锁,unique_lock对象销毁时自动调用析构函数解锁,可以很方便的防止死锁问题。

  • 上锁/解锁操作:lock、try_lock、try_lock_for、try_lock_until和unlock
  • 修改操作:移动赋值交换(swap:与另一个unique_lock对象互换所管理的互斥量所有权)、释放(release:返回它所管理的互斥量对象的指针,并释放所有权)
  • 获取属性:owns_lock(返回当前对象是否上了锁)、operator bool()(与owns_lock()的功能相同)、mutex(返回当前unique_lock所管理的互斥量的指针)。

2.4 原子操作库(atomic)

多线程最主要的问题是共享数据带来的问题(即线程安全)。如果共享数据都是只读的,那么没问题,因为只读操作不会影响到数据,更不会涉及对数据的修改,所以所有线程都会获得同样的数据。但是,当一个或多个线程要修改共享数据时,就会产生很多潜在的麻烦。

#include <iostream>
#include <thread>
using namespace std;
unsigned long sum = 0L;
void fun(size_t num)
{for (size_t i = 0; i < num; ++i)sum++;
}
int main()
{cout << "Before joining,sum = " << sum << std::endl;thread t1(fun, 10000000);thread t2(fun, 10000000);t1.join();t2.join();cout << "After joining,sum = " << sum << std::endl;return 0;
}

以上问题虽然加锁可以解决,但是加锁有一个缺陷就是:只要一个线程在对sum++时,其他线程就会被阻塞,会影响程序运行的效率,而且锁如果控制不好,还容易造成死锁。因此C++11中引入了原子操作。所谓原子操作:即不可被中断的一个或一系列操作,C++11引入
的原子操作类型,使得线程间数据的同步变得非常高效。

使用atomic类模板,定义出需要的任意原子类型。

atmoic<T> t;    // 声明一个类型为T的原子类型变量t

注意:原子类型通常属于"资源型"数据,多个线程只能访问单个原子类型的拷贝,因此在C++11中,原子类型只能从其模板参数中进行构造,不允许原子类型进行拷贝构造、移动构造以及operator=等,为了防止意外,标准库已经将atmoic模板类中的拷贝构造、移动构造、赋值运算符重载默认删除掉了。

————————————————————
感谢大家观看,不妨点赞支持一下吧[doge]
如有错误,随时纠正,谢谢大家

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

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

相关文章

eNSP-抓包解析TCP三次握手和四次挥手的过程

一、环境搭建 1.设备连接 并 启动所有设备 2.服务器配置 3.客服端配置 二、抓包测试 1.打开抓包软件 2.客户端获取数据 三、抓包结果

HEC-HMS水文模型

HEC-HMS是美国陆军工程兵团水文工程中心开发的一款水文模型。HMS能够模拟各种类型的降雨事件对流域水文&#xff0c;河道水动力以及水利设施的影响&#xff0c;在世界范围内得到了广泛的应用。它有着完善的前后处理软件&#xff0c;能有效减轻建模的负担&#xff1b;能够与HEC开…

如何用Vue实现实时网络状态监控:一篇让你轻松掌握前端网络连通性管理的指南

1、演示 2、网络监控目的 网络性能优化&#xff1a; 通过监控用户的网络状态&#xff0c;可以了解网络延迟、带宽利用率、丢包率等信息&#xff0c;从而优化网络性能&#xff0c;提升用户体验。 故障排除&#xff1a; 可以监控网络状态以及网络设备的运行情况&#xff0c;及时…

【现代C++】线程支持库

现代C&#xff08;C11及其之后的版本&#xff09;引入了标准的线程支持库&#xff0c;使得多线程编程变得更加简单和可移植。这个库提供了线程管理、互斥量、条件变量和其他同步原语。 1. std::thread - 基本线程 std::thread允许创建执行特定任务的线程。 #include <ios…

蓝桥杯-油漆面积

代码及其解析:(AC80%&#xff09; 思路:是把平面划成单位边长为1&#xff08;面积也是1&#xff09;的方格。每读入一个矩形&#xff0c;就把它覆盖的方格标注为已覆盖&#xff1b;对所有矩形都这样处理&#xff0c;最后统计被覆盖的方格数量即可。编码极其简单&#xff0c;但…

gradio简单搭建——关键词匹配筛选【进一步优化】

gradio简单搭建——关键词匹配筛选[进一步优化] 任务回顾新的想法&#xff1a;无效元素筛选界面搭建数据处理与生成过程交互界面展示 任务回顾 在 apply \text{apply} apply方法的使用一节中&#xff0c;简单提到了任务目标&#xff1a;通过关键词的形式&#xff0c;在文本数据…

三维点云:对原始点云数据进行体素化

文章目录 一、原始点云二、对原始点云进行体素化三、结果展示 一、原始点云 &#x1f349;原始点云为.pts文件&#xff0c;内容为x, y, z的坐标 原始点云展示 二、对原始点云进行体素化 使用open3d库实现&#xff0c;如果没有需要在命令行执行pip install open3d import o…

【STL】vector

目录 1. vector的使用 1.1 vector的定义 1.2 vector iterator 的使用 1.3 vector 空间增长问题 1.4 vector 增删查改 1.5 vector 迭代器失效问题&#xff08;重点&#xff09; 2.vector模拟实现 1. vector的使用 1.1 vector的定义 1.2 vector iterator 的使用 1.3 vecto…

【PDF-XSS攻击】Java项目-上传文件-解决PDF文件XSS攻击

文章目录 背景解决pdfbox依赖控制器代码PdfUtils工具类 验证最后源码参考 背景 上传xss-pdf造成存储型xss因为在浏览器直接预览的PDF&#xff0c;而不是预览&#xff0c;所以安全部门认为会有XSS漏洞 解决 安全部门修复建议 1、根据白名单的标签和属性对数据进行过滤&#…

linux大文件IO

在Linux中处理大文件&#xff08;通常指大小超过2GB的文件&#xff09;时&#xff0c;需要使用特定的系统调用和标志&#xff0c;以确保程序能够正确地处理大文件的读写。这主要是因为在32位系统上&#xff0c;传统的文件偏移量和文件大小使用off_t类型表示&#xff0c;它通常是…

揭秘ChatGPT预训练数据集

自大语言模型引领新一代的AI浪潮之后&#xff0c;对于Open AI发布的GPT系列LLM使用的数据集一直是行业内的谜&#xff0c;我们都知道&#xff0c;随着模型的参数量提升&#xff0c;预训练数据的使用量也同步增加&#xff0c;下面就让我们从相关论文和分析从探索GPT-X大模型的预…

地理信息系统(ArcGIS)在水文水资源、水环境中的应用

刘老师&#xff08;副教授&#xff09;&#xff1a;来自北京重点高校资深专家&#xff0c;长期从事水资源与水环境、流域污染控制与管理、非点源模拟与控制、环境信息系统开发、环境遥感与GIS应用等领域的研究&#xff0c;发表多篇Sci论文、具有资深的技术底蕴和专业背景。 1、…

wps可以打钩的框框

方法一&#xff1a; 输入2611&#xff0c;按下altx 方法二&#xff1a; R 选中后->开始->字体wingdings字体

自动驾驶硬件系统-激光雷达(Lidar)测量模型

自动驾驶硬件系统-激光雷达(Lidar)测量模型 激光雷达(Lidar, Light Detection And Ranging)是Google系自动驾驶技术路线广泛应用的硬件传感器。 附赠自动驾驶学习资料和量产经验&#xff1a;链接 1、激光雷达(Lidar)的工作原理 通过持续不断的发射激光束&#xff0c;激光束遇…

winform入门篇3 -- 手工创建窗口

手工创建窗口 Form, 窗口 可以手工创建一个窗口类 class MyFrom : Form { } 1.创建一个windows 窗体应用 这样就自动创建了一个窗体应用Form1 现在不使用这个自动创建的&#xff0c;手工写一个 2.手动创建 1.删除Form1.cs 2.添加 新建MyForm 类 让该类继承Form 在构造…

爬虫 新闻网站 以湖南法治报为例(含详细注释) V1.0

目标网站&#xff1a;湖南法治报 爬取目的&#xff1a;为了获取某一地区更全面的在湖南法治报已发布的宣传新闻稿&#xff0c;同时也让自己的工作更便捷 环境&#xff1a;Pycharm2021&#xff0c;Python3.10&#xff0c; 安装的包&#xff1a;requests&#xff0c;csv&#xff…

Unity类银河恶魔城学习记录12-8 p130 Skill Tree UI源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili UI.cs using UnityEngine;public class UI : MonoBehaviour {[SerializeFi…

Python:如何对FY3D TSHS的数据集进行重投影并输出为TIFF文件以及批量镶嵌插值?

完整代码见 Github&#xff1a;https://github.com/ChaoQiezi/read_fy3d_tshs&#xff0c;由于代码中注释较为详细&#xff0c;因此博客中部分操作一笔带过。 01 FY3D的HDF转TIFF 1.1 数据集说明 FY3D TSHS数据集是二级产品(TSHS即MWTS/MWHS 融合大气温湿度廓线/稳定度指数/…

【智能算法】省时方便,智能算法统计指标——一键运行~

目录 1.常用统计指标2.参数统计检验3.结果展示4.自定义修改测试框架 1.常用统计指标 测试智能算法性能时&#xff0c;常常会用到以下5种常用指标&#xff0c;简单不赘述&#xff1a; 最优值、最差值、均值、中位数、标准差 2.参数统计检验 单纯依靠常用统计指标说服力不足&…

结构型模式--3.组合模式【草帽大船团】

1. 好大一棵树 路飞在德雷斯罗萨打败多弗朗明哥之后&#xff0c;一些被路飞解救的海贼团自愿加入路飞麾下&#xff0c;自此组成了草帽大船团&#xff0c;旗下有7为船长&#xff0c;分别是&#xff1a; 俊美海贼团75人 巴托俱乐部56人 八宝水军1000人 艾迪欧海贼团4人 咚塔塔海…