【C++】多线程的学习笔记(2)——白话文版(bushi

2023-10-05 11:59

本文主要是介绍【C++】多线程的学习笔记(2)——白话文版(bushi,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

前一篇

本章内容提要

使用mutex锁的原因

mutex锁的概念

mutex的使用教程

锁的声明以及命名

mutex的加锁以及解锁

例子

结果

注意

mutex的其他方式的锁介绍

lock_guard

介绍

例子

运行结果

adopt_lock参数

unique_lock

介绍

try_to_lock

defer_lock

release

例子

结果

总结


前一篇

第一篇在这

【C++】多线程的学习笔记——白话文版(bushi-CSDN博客C++ 作为一种强大的编程语言,为多线程编程提供了丰富而灵活的支持。C++ 的标准库提供了头文件,其中包含了用于创建、启动和管理线程的类和函数。通过使用这些多线程库和功能,开发人员可以轻松地引入并发性到自己的应用程序中,实现多线程的并行处理。thread函数中定义线程的语法规如下std::thread 变量名 (函数,传递的参数1,传递的参数2,传递的参数3...)【如果前面加了using namespace std;可以删除std::】https://blog.csdn.net/mumuemhaha/article/details/133468825?spm=1001.2014.3001.5502

本章内容提要

上一章我们讲解了如何利用thread库初步进行多线程操作

这一章,我们主要讲的是锁(其实就是mutex锁)的概念

使用mutex锁的原因

在上一章的多线程操作中我们也许会想到一个问题——如果变量或者资源他不是独占的,而是共享的(比如对于全局变量的修改),那么如果多个线程同时访问就会引起不可预料的错误

这个时候就必须要给线程进行加锁确保只能有一个线程运行此函数。

mutex锁的概念

Mutex(互斥锁)是一种线程同步机制,用于保护共享资源的访问,防止多个线程同时访问和修改同一份数据而引发竞争条件(race condition)。

Mutex 的作用是在关键代码段前后加锁和解锁操作,确保只有一个线程能够进入临界区(critical section)执行代码,从而保证共享资源的安全访问。

同一时刻,同一临界区,只能有一个线程持有该锁

mutex的使用教程

锁的声明以及命名

开头必然要声明库函数

#include <mutex>

和其他类型的变量一样,之后锁还需要声明一个变量

mutex mtx_1;

这个最好是在全局变量中进行声明

mutex的加锁以及解锁

在你写函数需要加锁时你只需要调用他们当中的lock(),以及unlck(),如果在执行lock时候如果锁已经被其他线程获取了,那么线程会进行等待

拿上面的进行举例就是

mtx_1.lock();//加锁
mtx_1.unlock();//解锁

例子

运行一个

#include <iostream>
#include <thread>
#include <time.h>
#include <vector>
#include <mutex>
using namespace std;
mutex mtx_1;
void F_1(int i) {mtx_1.lock();cout << "This is NO." << i << " project is runing." << endl;this_thread::sleep_for(chrono::seconds(i));cout << "This is NO." << i << " project is finishing." << endl;mtx_1.unlock();
}
int main() {clock_t now_time_1 = clock();cout << "This project is start!" << endl;//记录刚刚开始的时间vector<thread>sum_1;for (int i = 1; i <= 3; i++) {sum_1.push_back(thread(F_1, i));}for (int i = 0; i <= sum_1.size() - 1; i++) {sum_1[i].join();}cout << "This project is ready!" << endl;//记录结束的时间clock_t now_time_2 = clock();cout << "The cost time is " << now_time_2 - now_time_1 << " ms " << endl;return 0;
}

简单的代码

结果

可能有人就要问,这不和之前顺序执行的时间一样吗?

先不要急,这只是举一个例子,例子也比较极端开头就锁上了,事实上你只需要在有资源冲突的函数部分加锁即可,其他的地方依旧可以和以前一样,甚至不同的函数你可以命名两个锁分别进行执行加锁或者是解锁。

换言之,锁只是在你需要确保该资源变量在同一时刻只被一个线程访问时加上即可。

注意

需要注意的是需要避免的是:两个或者多个线程之间所需要的资源被另外的线程锁住,从而造成死锁。

mutex的其他方式的锁介绍

lock_guard

介绍

lock_guard是模板类,对比于mutex的区别是lock_guard在创建时会尝试获得锁的所有权(注意时尝试,如果获取不到就相当于没有用,并且不会报错),在作用域结束时会自动析构,无需手动解锁

该类不可中途上锁和解锁,不可复制

例子

还是之前的代码

#include <iostream>
#include <thread>
#include <time.h>
#include <vector>
#include <mutex>
using namespace std;
mutex mtx_1;
void F_1(int i) {lock_guard<mutex>guard_1(mtx_1);cout << "This is NO." << i << " project is runing." << endl;this_thread::sleep_for(chrono::seconds(i));cout << "This is NO." << i << " project is finishing." << endl;
}
int main() {clock_t now_time_1 = clock();cout << "This project is start!" << endl;//记录刚刚开始的时间vector<thread>sum_1;for (int i = 1; i <= 3; i++) {sum_1.push_back(thread(F_1, i));}for (int i = 0; i <= sum_1.size() - 1; i++) {sum_1[i].join();}cout << "This project is ready!" << endl;//记录结束的时间clock_t now_time_2 = clock();cout << "The cost time is " << now_time_2 - now_time_1 << " ms " << endl;return 0;
}

运行结果

他并不需要解锁和解锁 

adopt_lock参数

adopt_lock用法为

lock_guard<mutex>guard_1(mtx_1,adopt_lock);

 加了这个参数,就可以在创建时候不上锁,代表表示这个互斥量已经lock();优化代码的运行时间,同时这个参数本质时起到一个标记

但是需要注意由于lock_guard不可以主动上锁,如果这个锁本身还没有lock过就会报错。

unique_lock

介绍

unique_lock的用法和lock_guard的用法类似,主要的区别在于他可以中途上锁以及解锁

对比于lock_guard会更加的灵活

但是所需要的内存空间会更大

同时它的也有adopt_lock参数用法一样,而且他还拥有其他的第二参数

try_to_lock

他会尝试的去获取锁,如果锁没有被占用就会获取到,如果已经被占用了也会立即放回执行下面的代码不会进行堵塞,用法和adopt_lock一样

defer_lock

创建锁的时候不上锁(需要注意区分前面的adopt_lock()这个时没上锁的前提下(如果上锁了会报错)创建该锁时不上锁。之后再进行上锁。),用法也和adopt_lock一样

release

为释放unique_lock的所有权,注意是释放——release!!!!不是解锁——unlock,之后的锁需要你自己来管理

例子

还是之前的代码中的函数

void F_1(int i) {unique_lock<mutex>guard_1(mtx_1);mutex* mtx_2 = guard_1.release();cout << "This is NO." << i << " project is runing." << endl;this_thread::sleep_for(chrono::seconds(i));cout << "This is NO." << i << " project is finishing." << endl;mtx_2->unlock();
}
结果

 当然还是一样的

总结

本章讲解了mutex大部分的知识点,使用时需要注意锁住的代码要尽可能的少而精准,这样程序的运行时间和稳定性以及安全性才可以同时得到显著的提升。

这篇关于【C++】多线程的学习笔记(2)——白话文版(bushi的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/133968

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]