C++11多线程库重点接口

目录

一.thread构造函数

二.移动构造,移动赋值

小结 

 三.获取线程id的方法

四.thread与lambda表达式联用

 五.Mutexs的总览

六.互斥锁 

七.Locks的总览

八. 条件变量总览

九.条件变量的wait和notify 

十.典型例题

十一.原子类

十二.智能指针和单例模式的线程安全问题


一.thread构造函数

void Print(int n)
{for (int i = 0; i < n; i++){cout << "I am a thread: " << i << endl;}
}
int main()
{thread t(Print, 1000);t.join();return 0;
}

如果想要传递引用,需要使用ref()函数将对象包装成引用以便向下传递。原因在于参数并不是直接传递给执行的方法,中间经过了thread的构造函数,为了完美转发保持属性而产生副作用,生成了一个拷贝的临时对象,则执行方法中引用的是临时对象,非const左值引用来引用临时对象是不被允许的。(简单说一说,底层非常复杂,不值得深究)。

如果用const引用来接收就不必使用ref

bind也是如此,如果想要传递引用就必须ref

二.移动构造,移动赋值

thread类的拷贝构造被禁用,但是有移动构造和移动赋值,以应对需要同时创建多个线程的场景

void Print(int j, int n)
{for (int i = j; i < n; i++){cout << "thread id: " << this_thread::get_id() << ", " << i << endl;}
}
int main()
{const int threadNum = 5;vector<thread> threads(threadNum);for (int i = 0; i < threads.size(); i++){threads[i] = thread(Print, 0, 1000); //匿名对象右值,调用移动赋值,减少拷贝}for (int i = 0; i < threads.size(); i++){threads[i].join();}return 0;
}//移动构造:
/*
for (int i = 0; i < threadNum; i++)
{threads.emplace_back(thread(Print, 0, 1000)); //emplace_back调用移动构造}
*/

思考:thread禁用了拷贝构造,有办法把用一个thread对象构造另一个对象吗? 

//使用移动构造:副作用:t1将不可用
thread t1(Print, 0, 1000);
thread t2(move(t1));

小结 

创建线程的方法:

  1. 用有参的构造传递方法和参数
  2. 创建多个线程用容器集中管理时,使用移动构造或者移动赋值
  3. 将一个线程的资源转移给另一个线程,move+移动构造

 三.获取线程id的方法

//线程的执行方法中获取:
this_thread::get_id();
//线程外获取:
t.get_id(); //t是创建的线程对象

四.thread与lambda表达式联用

lambda表达式的优势在于能捕获局部的变量,而无需传参

int x = 0;
int main()
{int n1 = 10000;int n2 = 10000;thread t1([n1](){for (int i = 0; i < n1; i++){x++;}});thread t2([n2](){for (int i = 0; i < n2; i++){x++;}});t1.join();t2.join();cout << x << endl;return 0;
}//注意:以上代码有线程安全问题

 五.Mutexs的总览

六.互斥锁 

mutex不支持拷贝,只能引用

int x = 0;
void Add(int n, mutex& mtx) //这里的互斥锁只能用引用接收,因为mutex禁用了拷贝构造
//mtx不能加const,否早无法lock,unlock
{mtx.lock();for (int i = 0; i < n; i++){x++;}mtx.unlock();
}
int main()
{mutex mtx;thread t1(Add, 10000, ref(mtx)); //想要传锁必须使用ref把mutex包装成一个引用对象,thread t2(Add, 10000, ref(mtx)); 
//因为mtx不是直接传给Add的,中间经过thread的构造函数,
//最终Add引用的是mtx的拷贝(完美转发保持属性的同时生成了一个拷贝的临时对象,简单说一说,不必深究)t1.join();t2.join();cout << x << endl;return 0;
}

七.Locks的总览

八. 条件变量总览

九.条件变量的wait和notify 

 十.典型例题

int main()
{//让t1和t2两个线程,从1开始,交替打印奇数和偶数int x = 1;mutex mtx;condition_variable cond;thread t1([&](){while (true){unique_lock<mutex> lck(mtx);while (x % 2 == 0){cond.wait(lck);}cout << "thread 1:" << x << endl;x++;Sleep(1000);cond.notify_one();}});thread t2([&](){while (true){unique_lock<mutex> lck(mtx);while (x % 2 == 1){cond.wait(lck);}cout << "thread 2:" << x << endl;x++;Sleep(1000);cond.notify_one();}});t1.join();t2.join();return 0;
}

十一.原子类

临界区操作非常少的情况,不适合加互斥锁。因为这会导致线程状态频繁切换,不断地阻塞和唤醒。此时就可以使用C++多线程库提供的atomic类来封装内置类型,使用atomic类提供的“原子”接口。这里的原子二字打了引号,因为严格来说它并非真正意义上的原子,只不过表现出来的是原子性的效果。当然日常交流中说它是原子操作也没问题。

首先需要明确一个概念:被保护后的临界区,也并不是原子的。原子操作是不可中断的操作,要么全部执行成功,要么全部不执行,没有中间状态。而临界区操作涉及多个指令或操作步骤,收线程调度影响,可能会被中断。只不过采用互斥的机制保护,使得任意时刻只有一个线程进入临界区,从而避免了数据不一致问题,达到了原子性的效果。

CAS操作:CAS即Comparre and Swap,它是CPU提供的原子操作。这才是真正意义上的原子操作,它由硬件支持,X86下对应的是CMPXCHG 汇编指令。

这个操作所做的工作是:先比较目标内存的值是否和预期的值一样(Compare),如果一样就进行赋值(Swap->把寄存器的值和目标内存的值进行交换),如果不一样就啥都不做

bool compare_and_swap (int *addr, int oldval, int newval)
{if ( *addr != oldval ) {return false;}*addr = newval;return true;
}

atomic的接口底层就是使用的CAS操作。

例如:

int a = 0;
a++;

atomic<int> a(0);
a++;

atomic或者说CAS操作适用于对公共资源操作非常简单的情况,例如对一个整型变量进行++,那么CAS的成功率就比较高,相较于加锁,可以减少线程状态切换的开销。但是如果操作比较多,还是加锁合适。

十二.智能指针和单例模式的线程安全问题

智能指针是线程安全的吗?

首先,unique_ptr通常用于独占所有权的情况,它被设计为独占所有权的智能指针,通常情况下只能由一个线程拥有和访问。因此,不建议多个线程使用同一个unique_ptr,其内部并没有提供线程安全保护机制,使用结果是未定义的。

其次,shared_ptr中的引用计数是会被公共访问的资源,涉及到++,--,判断等操作。shared_ptr内部提供了保护措施,要么是加锁保护,要么使用atomic这样的原子操作。

 

所以,虽然unique_ptr没有保护机制,但是人家设计的初衷就是用于独占资源的情况,不按要求使用那是你自讨苦吃,而shared_ptr内部有保护机制,可以供多个线程使用。

综上,智能指针是线程安全的,但是智能指针管理的资源不是线程安全的,需要用户自己保护。

 单例模式是线程安全的吗?

饿汉模式在main函数之前就创建了对象,没有线程安全问题。但是懒汉模式就有,调用获取实例的接口,首先会判断指向实例的指针是否为空,如果为空就创建实例。那么就有可能多个线程同时通过if判断,而创建出多个实例。

所以需要在判断之前加锁保护。

但是线程安全问题仅仅是第一次调用接口时才会出现,后续每次获取实例都要申请锁太消耗时间了,所以外层再套一个if判断,如果是空指针就加锁,否则直接返回。

其实懒汉模式还有另一种简单巧妙的实现方式,不把实例创建在堆区,而是创建静态对象。

这份代码在C++11之后是线程安全的,静态对象不管如何只会初始化一次,初始化静态对象是原子的。

但C++11之前它是线程不安全的,静态对象在多线程情况下初始化动作式未定义的。

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

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

相关文章

详解 Redis 在 Ubuntu 系统上的安装

在 Ubuntu 20.04 安装 Redis 1. 先切换到 root 用户 在 Ubuntu 20.04 中&#xff0c;可以通过以下步骤切换到 root 用户&#xff1a; 输入以下命令&#xff0c;以 root 用户身份登录&#xff1a; sudo su -按回车键&#xff0c;并输入当前用户的密码&#xff08;即具有 sudo…

【论文精读】Detecting Out-of-Distribution Examples with Gram Matrices 使用Gram矩阵检测分布外实例

文章目录 一、文章概览&#xff08;一&#xff09;Gram矩阵1、Gram&#xff08;格朗姆&#xff09;矩阵的定义2、Gram矩阵计算特征表示3、风格迁移中的Gram矩阵 &#xff08;二&#xff09;ood检测&#xff08;三&#xff09;核心思路&#xff1a;扩展 Gram 矩阵以进行分布外检…

2024最新在线工具箱/ 站长IT工具箱/网站系统源码下载

2024最新在线工具箱/ 站长IT工具箱/网站系统源码下载- 更多详情及下载地址请访问https://a5.org.cn/a5_ziyuan/39525.html 转载请注明出处!

SketchUp Pro 2024 for mac 草图大师 专业的3D建模软件

SketchUp Pro 2024 for Mac是一款功能强大的三维建模软件&#xff0c;适用于Mac电脑。其简洁易用的界面和强大的工具集使得用户可以轻松创建复杂的3D模型。 软件下载&#xff1a;SketchUp Pro 2024 for mac v24.0.483 激活版下载 SketchUp Pro 2024 for Mac支持导入和导出多种文…

软件杯 深度学习乳腺癌分类

文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度&#xff0c;召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…

性能测试,python 内存分析工具

Memray是一个由彭博社开发的、开源内存剖析器&#xff1b;开源一个多月&#xff0c;已经收获了超8.4k的star&#xff0c;是名副其实的明星项目。今天我们就给大家来推荐这款python内存分析神器。 Memray可以跟踪python代码、本机扩展模块和python解释器本身中内存分配&#xf…

Revit模型进入虚幻引擎UE5教程

一、背景 小伙伴们是否有Revit进入虚幻引擎交互的需求呢&#xff1f; 二、实现功能 1.Revit进入虚幻UE5,包含模型属性&#xff0c;材质等 2.实现BIM构件点选&#xff0c;高亮&#xff0c;属性展示 3.实现BIM模型分层显示&#xff0c;爆炸等效果 三、教程地址 教程&#x…

51单片机入门_江协科技_21~22_OB记录的笔记

21. LED点阵屏 21.1. LED点阵屏介绍 •LED点阵屏由若干个独立的LED组成&#xff0c;LED以矩阵的形式排列&#xff0c;以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合&#xff0c;如汽车报站器、广告屏以及公告牌等 •LED点阵屏分类 按颜色&#xff1a;单…

Leetcode 64. 最小路径和

心路历程&#xff1a; 第一反应像是一个回溯问题&#xff0c;但是看到题目中要求最值&#xff0c;大概率是一道DP问题。并且这里面的递推关系也很明显。 这里面边界条件可以有多种处理方法。 解法&#xff1a;动态规划 class Solution:def minPathSum(self, grid: List[List…

六、企业级架构缓存篇之memcached

一、memcached概述 1、网站架构优化流程&#xff1a; LNMP架构中网站应用访问流程&#xff1a; 浏览器 (app) → web 服务器 → 后端服务 (php) → 数据库 (mysql) 访问流程越多&#xff0c;访问速度越慢&#xff0c;出现问题的几率也越大。 网站访问流程优化思路&#xff1…

【机器学习】如何通过群体智慧解决机器学习的挑战“

机器学习的发展日新月异&#xff0c;但其成功实施的关键之一仍然是获取高质量的、标注良好的数据集。在这篇文章中&#xff0c;我们将探讨如何通过群体智慧来构建和改善机器学习的数据集&#xff0c;尤其是通过reCAPTCHA和带有目的的游戏&#xff08;Games with a Purpose, GWA…

比nestjs更优雅的ts控制反转策略-依赖查找

一、Cabloy5.0内测预告 Cabloy5.0采用TS对整个全栈框架进行了脱胎换骨般的大重构&#xff0c;并且提供了更加优雅的ts控制反转策略&#xff0c;让我们的业务开发更加快捷顺畅 1. 新旧技术栈对比&#xff1a; 后端前端旧版js、egg2.0、mysqljs、vue2、framework7新版ts、egg3…

Web大并发集群部署之集群介绍

一、传统web访问模型 传统web访问模型完成一次请求的步骤 1&#xff09;用户发起请求 2&#xff09;服务器接受请求 3&#xff09;服务器处理请求&#xff08;压力最大&#xff09; 4&#xff09;服务器响应请求 传统模型缺点 单点故障&#xff1b; 单台服务器资源有限&…

Prometheus+grafana环境搭建MongoDB(docker+二进制两种方式安装)(五)

由于所有组件写一篇幅过长&#xff0c;所以每个组件分一篇方便查看&#xff0c;前四篇mongodb的exporter坑也挺多总结一下各种安装方式&#xff0c;方便后续考古。 Prometheusgrafana环境搭建方法及流程两种方式(docker和源码包)(一)-CSDN博客 Prometheusgrafana环境搭建rabb…

【关于窗口移动求和的两种计算方法】

窗口移动计算方法 例子方法1方法2运行结果: 例子 在很多算法中都会涉及到窗口滑动&#xff0c;比如基于新息序列更新的自适应卡尔曼滤波器算法中便会使用到。 已知一个数列&#xff1a;OCV [1;2;3;4;5;6;7;8;9;10;11;12;13;14;15]&#xff0c;定义窗口长度为5&#xff0c;每次…

小林coding图解计算机网络|TCP篇06|如何理解TCP面向字节流协议、为什么UDP是面向报文的协议、如何解决TCP的粘包问题?

小林coding网站通道&#xff1a;入口 本篇文章摘抄应付面试的重点内容&#xff0c;详细内容还请移步&#xff1a;小林coding网站通道 文章目录 如何理解UDP 是面向报文的协议如何理解字节流如何解决粘包固定长度的消息 特殊字符作为边界自定义消息结构 如何理解UDP 是面向报文的…

Hadoop-入门

资料来源&#xff1a;尚硅谷-Hadoop 一、Hadoop 概述 1.1 Hadoop 是什么 1&#xff09;Hadoop是一个由Apache基金会所开发的分布式系统基础架构。 2&#xff09;主要解决&#xff1a;海量数据的存储和海量数据的分析计算问题。 3&#xff09;广义上来说&#xff0c;Hadoop…

深入浅出 -- 系统架构之分布式常见理论概念

随着计算机科学和互联网的发展&#xff0c;分布式场景变得越来越常见&#xff0c;能否处理好分布式场景下的问题&#xff0c;成为衡量一个工程师是否合格的标准。本文我们介绍下分布式系统相关的理论知识&#xff0c;这些理论是我们理解和处理分布式问题的基础。 CAP理论 CAP…

深入理解选择排序:算法原理、Java实现与性能优劣

算法学习的重要性 在程序员的世界里&#xff0c;算法就如同一座桥梁&#xff0c;连接着问题与解决方案&#xff0c;是实现优秀程序的关键。 掌握算法&#xff0c;就能够在面对各种问题时&#xff0c;找到最合适的解决方法&#xff0c;以最少的时间和空间&#xff0c;实现最优的…

Android数据存储技术

一、文件存储 <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:orientation"vertical"android:layout_width"match_parent"android:layout_height"match_parent" ><EditTextandroid:id&qu…