音视频学习笔记——c++多线程(二)

✊✊✊🌈大家好!本篇文章是多线程系列第二篇文章😇。首先讲解了利用mutex解决多线程数据共享问题,举例更好理解lockunlock的使用方法,以及错误操作造成的死锁问题,最后讲解了lock_guardunique_lock使用的注意事项。

c++多线程系列目录:

c++多线程(一)多进程和多线程并发**的区别以及各自优缺点,Thead线程库的基本使用。

对多线程其他内容感兴趣的同学可以点击上方目录链接跳转。


本专栏知识点是通过<零声教育>的音视频流媒体高级开发课程进行系统学习,梳理总结后写下文章,对音视频相关内容感兴趣的读者,可以点击观看课程网址:零声教育


🎡导航小助手🎡

    • 一、互斥量(Mutex)
      • 1.1 lock和unlock
      • 1.2 死锁
      • 1.3lock_guard与unique_lock
    • 二、小结

一、互斥量(Mutex)

   当多个线程同时访问同一个变量,并且其中至少有一个线程对该变量进行了写操作,那么就会出现数据竞争问题。数据竞争可能会导致程序崩溃、产生未定义的结果,或者得到错误的结果。
   为了避免数据竞争问题,需要使用同步机制来确保多个线程之间对共享数据的访问是安全的。常见的同步机制包括互斥量、条件变量、原子操作等。
   互斥量(mutex)是一种用于实现多线程同步的机制,用于确保多个线程之间对共享资源的访问互斥。互斥量通常用于保护共享数据的访问,以避免多个线程同时访问同一个变量或者数据结构而导致的数据竞争问题。

1.1 lock和unlock

mutex常用操作:

  • lock():资源上锁
  • unlock():解锁资源
  • trylock():查看是否上锁,它有下列3种类情况:
    • (1)未上锁返回false,并锁住;
    • (2)其他线程已经上锁,返回true
    • (3)同一个线程已经对它上锁,将会产生死锁

   死锁:在两个或两个以上的进程在执行过程中,由于竞争资源或者彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
下面举一个实例:
添加lock()和unlock():

	#include <iostream>#include <thread>#include <mutex>using namespace std;int shared_data = 0;mutex mtx;void func(int n) {for (int i = 0; i < 10; ++i) {mtx.lock();//添加lock锁shared_data++;        cout << "Thread " << n << " increment shared_data to " << shared_data <<endl;mtx.unlock();//解锁}}int main() {thread t1(func, 1);thread t2(func, 2);t1.join();t2.join();    cout << "Final shared_data = " << shared_data <<endl;    return 0;}

运行结果:
在这里插入图片描述
不添加:
在这里插入图片描述
结果就会很乱,因为两个线程都对shared_data进行操作,发生了数据竞争现象。

补充:什么是线程安全?
如果多线程程序每次的运行结果和单线程运行的结果始终是一样的,那么线程是安全的。

1.2 死锁

假设存在两个线程 T1 和 T2,都要对两个互斥量 mtx1 和 mtx2 进行访问,且按照以下顺序获取互斥量的所有权:

  • T1 先获取 mtx1 的所有权,再获取 mtx2 的所有权。
  • T2 先获取 mtx2 的所有权,再获取 mtx1 的所有权。

如果两个线程同时执行,就会出现死锁问题。
因为 T1 获取了 mtx1 的所有权,但是无法获取 mtx2 的所有权,而 T2 获取了 mtx2 的所有权,但是无法获取 mtx1 的所有权,两个线程互相等待对方释放互斥量,导致死锁。

为了解决这一问题,就需要两个线程按照相同的顺序获取互斥量的所有权。

	#include <iostream>#include <thread>#include <mutex>std::mutex mtx1, mtx2;void func1(){mtx2.lock(); std::cout << "Thread 1 locked mutex 2" << std::endl;    mtx1.lock();    std::cout << "Thread 1 locked mutex 1" << std::endl;    mtx1.unlock();    std::cout << "Thread 1 unlocked mutex 1" << std::endl;    mtx2.unlock();    std::cout << "Thread 1 unlocked mutex 2" << std::endl;}void func2() {    mtx2.lock();    std::cout << "Thread 2 locked mutex 2" << std::endl;    mtx1.lock();    std::cout << "Thread 2 locked mutex 1" << std::endl;    mtx1.unlock();    std::cout << "Thread 2 unlocked mutex 1" << std::endl;    mtx2.unlock();    std::cout << "Thread 2 unlocked mutex 2" << std::endl;}int main(){    std::thread t1(func1);    std::thread t2(func2);    t1.join();    t2.join();    return 0;}

运行结果:
在这里插入图片描述

1.3lock_guard与unique_lock

lock_guard
创建lock_guard对象时,它将尝试获取提供给它的互斥锁的所有权。当控制流离开lock_guard对象的作用域时,lock_guard析构并释放互斥量。
lock_guard的特点:

  • 当构造函数被调用时,该互斥量会被自动锁定
  • 当析构函数被调用时,该互斥量会被自动解锁
  • std::lock_guard 对象不能复制或移动,因此它只能在局部作用域中使用。

代码举例:

	#include <thread>#include <mutex>#include <iostream>int g_i = 0;std::mutex g_i_mutex; // protects g_i,用来保护g_ivoid safe_increment() {const std::lock_guard<std::mutex> lock(g_i_mutex);++g_i;std::cout << std::this_thread::get_id() << ": " << g_i << '\n';// g_i_mutex自动解锁}int main() {std::cout << "main id: " << std::this_thread::get_id() << std::endl;std::cout << "main: " << g_i << '\n';std::thread t1(safe_increment);std::thread t2(safe_increment);t1.join(); t2.join();std::cout << "main: " << g_i << '\n';}

运行结果:

在这里插入图片描述
最开始,主线程id17336g_i0,每经过一个线程,g_i++

unique_lock
简单地讲,unique_lock lock_guard 的升级加强版,它具有 lock_guard 的所有功能,同时又具有其他很多方法,它可以对互斥量进行更加灵活的管理,包括延迟加锁、条件变量、超时等。
unique_lock的特点:

  • 创建时可以不锁定(通过指定第二个参数为std::defer_lock),而在需要时再锁定
  • 可以随时加锁解锁
  • 作用域规则同 lock_grard,析构时自动释放锁
  • 不可复制,可移动
  • 条件变量需要该类型的锁作为参数(此时必须使用unique_lock)

std::unique_lock 提供了以下几个成员函数:

  • lock():尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁
  • try_lock():尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则函数立即返回 false,否则返回 true
  • try_lock_for(const std::chrono::duration<Rep, Period>& rel_time):尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁,或者超过了指定的时间
  • try_lock_until(const std::chrono::time_point<Clock, Duration>& abs_time):尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁,或者超过了指定的时间点
  • unlock():对互斥量进行解锁操作
	#include <thread>#include <mutex>#include <iostream>int g_i = 0;std::mutex mtx;void func() {for (int i = 0; i < 10; i++) {std::unique_lock<std::mutex> lg(mtx);//知识点1.构造但不加锁,需要自己加锁//std::unique_lock<std::mutex> lg(mtx,std::defer_lock);g_i++;}}//知识点2,延时加锁std::timed_mutex  mtx1;  //需要使用时间锁void func1(){for (int i = 0; i < 2; i++) {std::unique_lock<std::timed_mutex> lg(mtx1, std::defer_lock);//知识点2,延时加锁if (lg.try_lock_for(std::chrono::seconds(2))) {std::this_thread::sleep_for(std::chrono::seconds(1));g_i++;}}}int main() {std::thread t1(func1);std::thread t2(func1);t1.join();t2.join();std::cout << g_i << '\n';}

总之,一定要记住。unique_lock会在构建的时候可以选择是否进行加锁,析构的时候会解锁,并且可以选择延迟加锁。

二、小结

  1. 互斥量(mutex)是一种用于实现多线程同步的机制,用于确保多个线程之间对共享资源的访问互斥。互斥量通常用于保护共享数据的访问,以避免多个线程同时访问同一个变量或者数据结构而导致的数据竞争问题。
  2. 常常使用lock和unlock进行上锁和解锁,错误的行为有时会造成死锁,这就要要求两个线程按照相同的顺序获取互斥量的所有权。
  3. 创建lock_guard对象时,它会自动上锁,析构时自动解锁,比较方便。
  4. unique_lock**会在构建的时候可以选择是否进行加锁,析构的时候会解锁,并且可以选择延迟加锁。适用范围更广。

感谢大家阅读!
接下来还会继续更新多线程相关知识,感兴趣的可以看其他笔记!

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

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

相关文章

Django模型层(附带test环境)

Django模型层(附带test环境) 目录 Django模型层(附带test环境)连接数据库Django ORM在models.py中建表允许为空指定默认值数据库迁移命令 开启测试环境建表语句补充(更改默认表名)数据的增加时间数据的时区 多表数据的增加一对多多对多 数据的删除修改数据查询数据查询所有数据…

Linux unset命令教程:如何删除环境变量或函数(附实例详解和注意事项)

Linux unset命令介绍 unset命令用于删除环境变量或函数。如果你不再需要某个环境变量或函数&#xff0c;你可以使用unset命令将其删除。 Linux unset命令适用的Linux版本 unset命令在大多数Linux发行版中都是可用的&#xff0c;包括Debian、Ubuntu、Alpine、Arch Linux、Kal…

鸿蒙OpenHarmony HDF 驱动开发

目录 序一、概述二、HDF驱动框架三、驱动程序四、驱动配置坚持就有收获 序 最近忙于适配OpenHarmonyOS LiteOS-M 平台&#xff0c;已经成功实践适配平台GD32F407、STM32F407、STM32G474板卡&#xff0c;LiteOS适配已经算是有实际经验了。 但是&#xff0c;鸿蒙代码学习进度慢下…

【AcWing】蓝桥杯集训每日一题Day1|二分|差分|503.借教室(C++)

503. 借教室 503. 借教室 - AcWing题库难度&#xff1a;简单时/空限制&#xff1a;1s / 128MB总通过数&#xff1a;8052总尝试数&#xff1a;26311来源&#xff1a;NOIP2012提高组算法标签二分差分 题目内容 在大学期间&#xff0c;经常需要租借教室。 大到院系举办活动&…

基于机器学习的网络入侵检测二元分类模型构建与性能评估(NSL-KDD数据集)

简介 该项目是一个基于NSL-KDD数据集的网络入侵检测系统&#xff0c;主要采用机器学习方法对网络流量数据进行使用了多种机器学习模型&#xff0c;如逻辑回归、线性SVM、多项式核SVM、高斯核SVM、决策树、随机森林、朴素贝叶斯和K近邻算法训练二元分类&#xff08;正常/异常&a…

bug总结(1)--变量取错

a c t i v i t y [ ′ t a g n a m e ′ ] 应为 activity[tag_name]应为 activity[′tagn​ame′]应为couponActivitList[0][‘name’] .隐藏的bug&#xff0c;在测试中竟然测不出来&#xff0c;而且上线了好久。为啥会出现这种低级错误呢&#xff1f;第一是写的时候不够仔细认…

Day26:安全开发-PHP应用模版引用Smarty渲染MVC模型数据联动RCE安全

目录 新闻列表 自写模版引用 Smarty模版引用 代码RCE安全测试 思维导图 PHP知识点&#xff1a; 功能&#xff1a;新闻列表&#xff0c;会员中心&#xff0c;资源下载&#xff0c;留言版&#xff0c;后台模块&#xff0c;模版引用&#xff0c;框架开发等 技术&#xff1a;输…

Selenium库快速查找网页元素及执行浏览器模拟操作

Selenium 是一个自动化测试工具&#xff0c;主要用于模拟用户在网页上的行为&#xff0c;进行自动化测试。它支持多种浏览器&#xff0c;并且可以在多种操作系统上运行。以下是 Selenium 库的一些主要特点和用途&#xff1a; 网页自动化测试&#xff1a; Selenium 可以模拟用户…

2024 年中国高校大数据挑战赛赛题 C:用户对博物馆评论的情感分析完整思路以及源代码分享

博物馆是公共文化服务体系的重要组成部分。国家文物局发布&#xff0c; 2021 年我国新增备案博物馆 395 家&#xff0c;备案博物馆总数达 6183 家&#xff0c;排 名全球前列&#xff1b;5605 家博物馆实现免费开放&#xff0c;占比达 90%以上&#xff1b;全国 博物馆举办展览 3…

【深度学习笔记】6_5 RNN的pytorch实现

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 6.5 循环神经网络的简洁实现 本节将使用PyTorch来更简洁地实现基于循环神经网络的语言模型。首先&#xff0c;我们读取周杰伦专辑歌词…

Python操作Redis 各种数据类型

本文将深入探讨如何使用Python操作Redis&#xff0c;覆盖从基础数据类型到高级功能的广泛主题。无论是字符串、列表、散列、集合还是有序集合&#xff0c;我们将一一解析&#xff0c;同时提供丰富的代码示例帮助读者更好地理解和应用。除此之外&#xff0c;本文还将介绍Redis的…

JVM和JVM内存管理

Java虚拟机&#xff08;Java Virtual Machine 简称JVM&#xff09;是运行所有Java程序的抽象计算机&#xff0c;是Java语言的运行环境&#xff0c;它是Java 最具吸引力的特性之一。Java源代码经过编译器编译后生成与平台无关的字节码文件&#xff08;.class文件&#xff09;。当…

【20240309】WORD宏设置批量修改全部表格格式

WORD宏设置批量修改全部表格格式 引言1. 设置表格文字样式2. 设置表格边框样式3. 设置所有表格边框样式为075pt4. 删除行参考 引言 这两周已经彻底变为office工程师了&#xff0c;更准确一点应该是Word工程师&#xff0c;一篇文档动不动就成百上千页&#xff0c;表格图片也是上…

计算机视觉(CV)自然语言处理(NLP)大模型应用,如何实现小模型

在人工智能领域&#xff0c;大模型已经成为引领创新和进步的重要推动力。它们不仅在自然语言处理、计算机视觉等任务中展现了强大的性能&#xff0c;还为各行各业带来了前所未有的机遇和挑战。本文将从一个高级写作专家的角度&#xff0c;深入探讨大模型的现状、技术突破以及未…

STM32之串口中断接收UART_Start_Receive_IT

网上搜索了好多&#xff0c;都是说主函数增加UART_Receive_IT()函数来着&#xff0c;实际正确的是UART_Start_Receive_IT()函数。 —————————————————— 参考时间&#xff1a;2024年3月9日 Cube版本&#xff1a;STM32CubeMX 6.8.1版本 参考芯片&#xff1a…

Svg Flow Editor 原生svg流程图编辑器(二)

系列文章 Svg Flow Editor 原生svg流程图编辑器&#xff08;一&#xff09; 说明 这项目也是我第一次写TS代码哈&#xff0c;现在还被绕在类型中头昏脑胀&#xff0c;更新可能会慢点&#xff0c;大家见谅~ 目前实现的功能&#xff1a;1. 元件的创建、移动、形变&#xff1b;2…

完全背包问题(一般写法与空间优化写法)

题目 有 N 种物品和一个容量是 V 的背包&#xff0c;每种物品都有无限件可用。 第 i 种物品的体积是 vi&#xff0c;价值是 wi。 求解将哪些物品装入背包&#xff0c;可使这些物品的总体积不超过背包容量&#xff0c;且总价值最大。 输出最大价值。 输入格式 第一行两个整…

最多几个直角三角形python

最多几个直角三角形 问题描述思路代码实现 问题描述 最多可以组成几个直角三角形&#xff0c;一个边只能用一次。 输入描述&#xff1a; 第一行输入一个正整数T&#xff08;1<&#xff1d;T<&#xff1d;100&#xff09;&#xff0c;表示有T组测试数据. 对于每组测试数据…

贪心算法: 奶牛做题

5289. 奶牛做题 - AcWing题库 贝茜正在参加一场奶牛智力竞赛。 赛事方给每位选手发放 n 张试卷。 每张试卷包含 k 道题目&#xff0c;编号 1∼k。 已知&#xff0c;不同卷子上的相同编号题目的难度相同&#xff0c;解题时间也相同。 其中&#xff0c;解决第 i 道题&#xff08;…

【C语言】字符指针

在指针的类型中我们知道有一种指针类型为字符指针char* 一般使用&#xff1a; int main() { char ch w; char *pc &ch; *pc w; return 0; } 还有一种使用方式&#xff0c;如下&#xff1a; int main() { const char* pstr "hello bit.";//这⾥是把⼀个字…