当前位置: 首页 > news >正文

【linux学习指南】线程同步与互斥

请添加图片描述

文章目录

  • 📝线程互斥
  • 🌠 库函数strncpy
    • 🌉进程线程间的互斥相关背景概念
    • 🌉互斥量mutex
  • 🌠线程同步
    • 🌉条件变量
    • 🌉同步概念与竞态条件
    • 🌉 条件变量函数
  • 🚩总结


📝线程互斥

🌠 库函数strncpy

🌉进程线程间的互斥相关背景概念

  • 临界资源:多线程执⾏流共享的资源就叫做临界资源
  • 临界区:每个线程内部,访问临界资源的代码,就叫做临界区
  • 互斥:任何时刻,互斥保证有且只有⼀个执⾏流进⼊临界区,访问临界资源,通常对临界资源起保护作⽤
  • 原⼦性(后⾯讨论如何实现):不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成

🌉互斥量mutex

  • ⼤部分情况,线程使⽤的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程⽆法获得这种变量。
  • 但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互。
  • 多个线程并发的操作共享变量,会带来⼀些问题。

Makefile文件

bin=ticket
cc=g++
src=$(wildcard *.cc)
obj=$(src:.cc=.o)$(bin):$(obj)$(cc) -o $@ $^ -lpthread
%.o:%.cc@echo "Comiling $< to $@"$(cc) -c $< -std=c++17.PHONY:clean
clean:rm -f $(bin) $(obj).PHONY:test
test:echo $(src)echo $(obj)

代码:

#include <stdio.h>
#include <string>
#include <string.h>
#include <pthread.h>
#include <unistd.h>int ticket = 100;void* routine(void* args)
{char *id = (char*)args;// std::string id = static_cast<const char*>(args);while(1){if(ticket > 0){usleep(10000);printf("%s sells ticket:%d\n", id, ticket);ticket--;}else{break;}}return nullptr;
}int main(void)
{pthread_t t1, t2 , t3, t4;pthread_create(&t1, nullptr, routine, (void*)"thread 1");pthread_create(&t2, nullptr, routine, (void*)"thread 2");pthread_create(&t3, nullptr, routine, (void*)"thread 3");pthread_create(&t4, nullptr, routine, (void*)"thread 4");pthread_join(t1, nullptr);pthread_join(t2, nullptr);pthread_join(t3, nullptr);pthread_join(t4, nullptr);return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • if语句判断条件为真以后,代码可以并发的切换到其他进程
  • usleep这个模拟夜漫长业务的过程这个漫长的业务过程中,可能有多个线程会进入该代码段
  • --ticket操作本身就不是一个原子操作

取出ticket–部分的汇编代码

objdump -d a.out > test.objdump152   40064b:   8b 05 e3 04 20 00     mov       0x2004e3(%rip),%eax 
600b34 <ticket>153   400651:   83 e8 01                	 sub        $0x1,%eax
154   400654:   89 05 da 04 20 00       mov		%eax,0x2004da(%rip)  
600b34 <ticket>

--操作并不是原子操作而是对应三条汇编指令:

  • load将共享变量体的从内存加载到寄存器
  • update更新寄存器里面的只执行复议操作
  • store:将新值从寄存器写回共享变量ticket的内存地址

要解决以上问题需要做到三点:

  • 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他进程进入该临界区
  • 如果多个线程同时要求进入临界区的代码,并且临界区没有线程在执行,那么只能一个线程进入该临界区
  • 如果现场不在临界区中执行,那么该现场就不能阻止其他进程进入临界区

要做到这三点,本身是上就是需要一把锁,linux上提供这把锁叫互斥量
在这里插入图片描述
互斥量的接口
初始化互斥量的两种方法

  • 方法1:静态分配
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER 
  • ⽅法2,动态分配:
    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
    参数:
    mutex:要初始化的互斥量
    attr:这是一个指向 pthread_mutexattr_t 类型对象的指针,该类型用于定义互斥锁的属性。如果将其设置为 NULL

销毁互斥量
销毁互斥量需要注意:

  • 使用PTHREAD_ MUTEX_ INITIALIZER初始化的互斥量不需要销毁
  • 不要销毁⼀个已经加锁的互斥量
  • 已经销毁的互斥量,要确保后⾯不会有线程再尝试加锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);

互斥量加锁和解锁

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回o,失败返回错误号

用pthread_ lock时,可能会遇到以下情况:

  • 互斥量处于未锁状态,该函数会将互斥量锁定,同时返回成功
  • 发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但没有竞争到互斥量,那么pthread_lock调用会陷入阻塞(执行流被挂起),等待互斥量解锁。

改进上面的售票系统:

#include <stdio.h>
#include <string>
#include <string.h>
#include <pthread.h>
#include <unistd.h>int ticket = 100;
pthread_mutex_t mutex;void* routine(void* args)
{char *id = (char*)args;// std::string id = static_cast<const char*>(args);while(1){pthread_mutex_lock(&mutex);if(ticket > 0){usleep(1000);printf("%s sells ticket:%d\n", id, ticket);ticket--;pthread_mutex_unlock(&mutex);}else{pthread_mutex_unlock(&mutex);break;}}return nullptr;
}int main(void)
{pthread_t t1, t2 , t3, t4;pthread_create(&t1, nullptr, routine, (void*)"thread 1");pthread_create(&t2, nullptr, routine, (void*)"thread 2");pthread_create(&t3, nullptr, routine, (void*)"thread 3");pthread_create(&t4, nullptr, routine, (void*)"thread 4");pthread_join(t1, nullptr);pthread_join(t2, nullptr);pthread_join(t3, nullptr);pthread_join(t4, nullptr);pthread_mutex_destroy(&mutex);return 0;
}

在这里插入图片描述
在这里插入图片描述

RAII风格的互斥锁,C++11也有,比如:
std : : mutex mtx;
std : : lock_guard guard ( mtx ) ;

🌠线程同步

🌉条件变量

  • 当⼀个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。
  • 例如⼀个线程访问队列时,发现队列为空,它只能等待,只到其它线程将⼀个节点添加到队列中。这种情况就需要⽤到条件变量。

🌉同步概念与竞态条件

  • 同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从⽽有效避免饥饿问题,叫做同步
  • 竞态条件:因为时序问题,⽽导致程序异常,我们称之为竞态条件。在线程场景下,这种问题也
    不难理解

🌉 条件变量函数

初始化

int pthread_cond_init(pthread_cond_t *restrict cond,   const pthread_condattr_t*restrict attr);

参数:
cond:要初始化的条件变量
attr: NULL

销毁:

int pthread_cond_destroy(pthread_cond_t *cond)

等待条件满⾜

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrictmutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量,后面详细解释

唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

简单案例:

  • 我们先使用PTHREAD_COND/MUTEX_INITIALIZER进行测试,对其他细节暂不追究
  • 然后将接口更改成为使用pthread_cond_init/pthread_cond_destroy的方式,方便后续进行封装
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void* active(void* args)
{std::string name = static_cast<const char*>(args);while(true){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex);std::cout<< name << "活动..." << std::endl;pthread_mutex_unlock(&mutex);}
}int main()
{pthread_t t1, t2;pthread_create(&t1, nullptr, active, (void*)"thread -1");pthread_create(&t2, nullptr, active, (void*)"thread -2");sleep(3);while(true){//对比测试pthread_cond_signal(&cond);//唤醒一个线程// pthread_cond_broadcast(&cond);//唤醒所有线程sleep(1);}pthread_join(t1, nullptr);pthread_join(t2, nullptr);pthread_cond_destroy(&cond);pthread_mutex_destroy(&mutex);return 0;
}

在这里插入图片描述


🚩总结

请添加图片描述

相关文章:

【linux学习指南】线程同步与互斥

文章目录 &#x1f4dd;线程互斥&#x1f320; 库函数strncpy&#x1f309;进程线程间的互斥相关背景概念&#x1f309;互斥量mutex &#x1f320;线程同步&#x1f309;条件变量&#x1f309;同步概念与竞态条件&#x1f309; 条件变量函数 &#x1f6a9;总结 &#x1f4dd;线…...

JavaScript函数与方法详解

目录 一、函数的定义 1. 函数声明 2. 函数表达式 3. 箭头函数 二、函数的调用 1. 调用方式 2. 参数数量的灵活性 三、arguments 对象 1. 基本概念 2. 属性 3. 应用场景 4. 转换为真数组 5. 总结 四、Rest参数 1. 基本概念 2. 特点 3. 应用场景 4. 总结 五、变…...

【论文笔记】ZeroGS:扩展Spann3R+GS+pose估计

spann3r是利用dust3r做了增量式的点云重建&#xff0c;这里zeroGS在前者的基础上&#xff0c;进行了增量式的GS重建以及进行了pose的联合优化&#xff0c;这是一篇dust3r与GS结合的具有启发意义的工作。 abstract NeRF和3DGS是重建和渲染逼真图像的流行技术。然而&#xff0c;…...

AtCoder - arc058_d Iroha Loves Strings解答与注意事项

链接&#xff1a;Iroha Loves Strings - AtCoder arc058_d - Virtual Judge 利用bitset这一数据结构&#xff0c;定义bitset类型的变量dp[i]表示第i到n个字符串能拼成的字符串长度都有哪些&#xff0c;比如00100101&#xff0c;表示能拼成的长度有0,2,5&#xff0c;&#xff0…...

企业使用统一终端管理(UEM)工具提高端点安全性

什么是统一终端管理(UEM) 统一终端管理(UEM)是一种从单个控制台管理和保护企业中所有端点的方法&#xff0c;包括智能手机、平板电脑、笔记本电脑、台式机和 IoT设备。UEM 解决方案为 IT 管理员提供了一个集中式平台&#xff0c;用于跨所有作系统和设备类型部署、配置、管理和…...

Leetcode 算法题 9 回文数

起因&#xff0c; 目的: 数学法。 % 求余数&#xff0c; 拆开组合&#xff0c;组合拆开。 这个题&#xff0c;翻来覆去&#xff0c;拆开组合&#xff0c; 组合拆开。构建的过程。 题目来源&#xff0c;9 回文数&#xff1a; https://leetcode.cn/problems/palindrome-number…...

设计模式Python版 命令模式(上)

文章目录 前言一、命令模式二、命令模式示例 前言 GOF设计模式分三大类&#xff1a; 创建型模式&#xff1a;关注对象的创建过程&#xff0c;包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。结构型模式&#xff1a;关注类和对象之间的组合&…...

C语言之循环结构:直到型循环

C语言 循环结构 直到型循环的实现 特点&#xff1a;先执行&#xff0c;后判断&#xff0c;不管条件是否满足&#xff0c;至少执行一次。典型代表&#xff1a;do…while&#xff0c;goto&#xff08;已淘汰&#xff0c;不推荐使用&#xff09; do…while 语法&#xff1a; d…...

细说STM32F407单片机RTC的备份寄存器原理及使用方法

目录 一、备份寄存器的功能 二、示例功能 三、项目设置 1、晶振、DEBUG、CodeGenerator、USART6 2、RTC 3、NVIC 4、GPIO 及KEYLED 四、软件设计 1、main.h 2、main.c 3、rtc.c 4、keyled.c、keyled.h 五、运行调试 本实例旨在介绍备份寄存器的作用。本实例继续使…...

MATLAB计算反映热需求和能源消耗的度数日指标(HDD+CDD)(全代码)

目录 度数日(Degree Days, DD)概述计算公式MATLAB计算代码调用函数1:计算单站点的 CDD参考度数日(Degree Days, DD)概述 度数日(Degree Days, DD)是用于衡量建筑、城市和地区的热需求和能源消耗模式的指标。它分为两部分: 加热度日(Heating Degree Days, HDD):当室…...

J6 X8B/X3C切换HDR各帧图像

1、OV手册上的切换命令 寄存器为Ox5074 各帧切换&#xff1a; 2、地平线control tool实现切换命令 默认HDR模式出图&#xff1a; HCG出图&#xff1a; LCG出图 SPD出图 VS出图...

09-轮转数组

给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 方法一&#xff1a;使用额外数组 function rotate(nums: number[], k: number): void {const n nums.length;k k % n; // 处理 k 大于数组长度的情况const newNums new A…...

用vue3写一个好看的wiki前端页面

以下是一个使用 Vue 3 Element Plus 实现的 Wiki 风格前端页面示例&#xff0c;包含现代设计、响应式布局和常用功能&#xff1a; <template><div class"wiki-container"><!-- 头部导航 --><el-header class"wiki-header"><d…...

瑞芯微烧写工具

文章目录 前言一、安装驱动二、安装烧写工具1.直接解压压缩包2. 如何使用 三、MASKROM 裸机必备四、LOADER 烧写&#xff0c;前提是搞过第三步没问题五、Update.img包的烧录六、linux下烧写总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 项目需要…...

说下JVM中一次完整的GC流程?

大家好&#xff0c;我是锋哥。今天分享关于【说下JVM中一次完整的GC流程?】面试题。希望对大家有帮助&#xff1b; 说下JVM中一次完整的GC流程? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 JVM中的一次完整的垃圾回收&#xff08;GC&#xff09;流程可以概括为…...

Open FPV VTX开源之OSD使用分类

Open FPV VTX开源之OSD使用分类 1. 源由2. 硬件2.1 【天空端】SigmaStar2.2 【天空端】Raspberry Pi2.3 【地面端】 3. 软件3.1 天空端软件3.2 地面端软件 4. 分类4.1 嵌入式OSD分类A1-嵌入式OSD&#xff1a;SigmaStar Android分类A2-嵌入式OSD&#xff1a;SigmaStar Hi3536分…...

智慧农业-虫害及生长预测

有害生物防控系统是一个综合性的管理体系&#xff0c;旨在预防和控制对人类生活、生产甚至生存产生危害的生物。这些生物可能包括昆虫、动物、植物、微生物乃至病毒等。 一、系统构成 1、监测预警系统&#xff1a;利用智能传感器、无人机、遥感技术等手段&#xff0c;实时监测…...

Python 识别图片和扫描PDF中的文字

目录 工具与设置 Python 识别图片中的文字 Python 识别图片中的文字及其坐标位置 Python 识别扫描PDF中的文字 注意事项 在处理扫描的PDF和图片时&#xff0c;文字信息往往无法直接编辑、搜索或复制&#xff0c;这给信息提取和分析带来了诸多不便。手动录入信息不仅耗时费…...

C语言如何知道当前系统中的编译器数据类型的大小是多少?

在 C 语言中&#xff0c;你可以使用sizeof运算符来确定当前系统中编译器数据类型的大小&#xff0c;该运算符返回一个size_t类型的值&#xff0c;表示所操作对象或数据类型占用的字节数。下面为你详细介绍使用方法&#xff1a; 1. 基本数据类型大小的获取 基本数据类型如char…...

gitlab Webhook 配置jenkins时“触发远程构建 (例如,使用脚本)”报错

报错信息&#xff1a; <html> <head> <meta http-equiv"Content-Type" content"text/html;charsetISO-8859-1"/> <title>Error 403 No valid crumb was included in the request</title> </head> <body><h2…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...

实战设计模式之模板方法模式

概述 模板方法模式定义了一个操作中的算法骨架&#xff0c;并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下&#xff0c;重新定义算法中的某些步骤。简单来说&#xff0c;就是在一个方法中定义了要执行的步骤顺序或算法框架&#xff0c;但允许子类…...

DAY 26 函数专题1

函数定义与参数知识点回顾&#xff1a;1. 函数的定义2. 变量作用域&#xff1a;局部变量和全局变量3. 函数的参数类型&#xff1a;位置参数、默认参数、不定参数4. 传递参数的手段&#xff1a;关键词参数5 题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一…...

《Offer来了:Java面试核心知识点精讲》大纲

文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...

简单介绍C++中 string与wstring

在C中&#xff0c;string和wstring是两种用于处理不同字符编码的字符串类型&#xff0c;分别基于char和wchar_t字符类型。以下是它们的详细说明和对比&#xff1a; 1. 基础定义 string 类型&#xff1a;std::string 字符类型&#xff1a;char&#xff08;通常为8位&#xff09…...