Linux线程:互斥锁mutex的使用

news2024/10/6 14:15:08

1. 同步 & 互斥

(1)同步

多个进程或线程按照一定的执行顺序完成某一任务;如B任务依赖A任务产生的数据。

(2)互斥

临界资源同一时刻只能被一个进程或线程使用。


2. 临界资源 和 临界区

(1)临界资源

一次只允许一个进程或线程访问的资源,互斥访问的资源。

(2)临界区

访问临界资源的代码段。多进程、多线程时,通常需要给临界区上锁。


3. 互斥锁mutex

临界区访问临界资源前加锁,对临界资源的操作完成后解锁;保证临界资源的互斥访问。

互斥锁相关函数

(1)互斥锁的初始化、销毁函数

#include<pthread.h>

int pthread_mutex_init(pthread_mutex_t* restrict mutex, const pthread_mutexattr_t* restrict attr);
/*
功能:
    初始化互斥锁mutex
参数:
    mutex:互斥锁地址
    attr:用来设置互斥锁的属性;通常为NULL。
    
    可使用宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,如:
        pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER,但此方法不会进行错误检查。
返回值:
    成功:0.
    失败:非0错误码。
*/

/* restrict关键字:表示指针所指的对象在指针的生命周期内不会被其他指针引用或修改;
                  以便让编译器更好地优化,避免不必要的内存访问或修改。    */

int pthread_mutex_destory(pthread_mutex_t* mutex);
/*
功能:
    销毁互斥锁mutex
参数:
    mutex:互斥锁的地址
返回值:
    成功:0
    失败:非0错误码
*/

(2)加、解锁(获取、释放锁)函数

#include<pthread.h>

/* 加锁 */
int pthread_mutex_lock(pthread_mutex_t* mutex);
/*
功能:
    加互斥锁mutex。若已被加锁,则阻塞至可以加锁。
参数:
    mutex:互斥锁地址
返回值:
    成功:0
    失败:非0错误码
*/

int pthread_mutex_trylock(pthread_mutex_t* mutex);
/*
功能:
    以非阻塞的方式加锁。
返回值:
    若临界区未被加锁,则直接加锁,返回0;
    若临界区已被加锁,则直接返回EBUSY。
*/

/* 解锁 */
int pthread_mutex_unlock(pthread_mutex_t* mutex);
/*
功能:
    解互斥锁mutex
参数:
    mutex:互斥锁地址
返回值:
    成功:0
    失败:非0错误码
*/

4. 互斥锁简单使用示例

如2个线程想使用同一台打印机,一个线程想打印Hello,另一个想打印World:

(1)未使用无互斥锁的打印机

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

void printer(char* str) { // 公共打印机
    while (*str != '\0') {
        putchar(*str);
        fflush(stdout); // 刷新输出缓冲区
        str++;
        usleep(1000);
    }
    putchar('\n');
}

void* task1(void* arg) { // 线程1的任务
    char* str = (char*)arg;
    printer(str);
    return NULL;
}

void* task2(void* arg) { // 线程2的任务
    char* str = (char*)arg;
    printer(str);
    return NULL;
}

int main(int argc, const char* argv[]) {

    pthread_t tid1, tid2;
    int ret = -1;

    char* str1 = "Hello";
    char* str2 = "World";

    ret = pthread_create(&tid1, NULL, task1, (void*)str1);
    if (0 != ret) {
        printf("线程1创建失败.\n");
        return 1;
    }

    ret = pthread_create(&tid2, NULL, task2, (void*)str2);
    if (0 != ret) {
        printf("线程2创建失败.\n");
        return 1;
    }

    ret = pthread_detach(tid1);
    if (0 != ret) {
        printf("线程1分离失败.\n");
        return 1;
    }

    ret = pthread_detach(tid2);
    if (0 != ret) {
        printf("线程2分离失败.\n");
        return 1;
    }
    sleep(1);

    return 0;
}

运行结果: 显然不符合预期。

(2)使用互斥锁的打印机

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

/*互斥锁。
若定义为main函数中的局部变量,
则需要通过pthread_create函数的第四个参数传递互斥锁*/
pthread_mutex_t mutex;

void printer(char* str) {
    while (*str != '\0') {
        putchar(*str);
        fflush(stdout); // 刷新输出缓冲区
        str++;
        usleep(1000);
    }
    putchar('\n');
}

void* task1(void* arg) {
    char* str = (char*)arg;
    pthread_mutex_lock(&mutex);
    printer(str);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void* task2(void* arg) {
    char* str = (char*)arg;
    pthread_mutex_lock(&mutex);
    printer(str);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main(int argc, const char* argv[]) {

    pthread_t tid1, tid2;
    int ret = -1;

    char* str1 = "Hello";
    char* str2 = "World";

    ret = pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
    if (0 != ret) {
        printf("互斥锁初始化失败.\n");
        return 1;
    }
    printf("互斥锁已初始化.\n");

    ret = pthread_create(&tid1, NULL, task1, (void*)str1);
    if (0 != ret) {
        printf("线程1创建失败.\n");
        return 1;
    }

    ret = pthread_create(&tid2, NULL, task2, (void*)str2);
    if (0 != ret) {
        printf("线程2创建失败.\n");
        return 1;
    }

    ret = pthread_detach(tid1);
    if (0 != ret) {
        printf("线程1分离失败.\n");
        return 1;
    }

    ret = pthread_detach(tid2);
    if (0 != ret) {
        printf("线程2分离失败.\n");
        return 1;
    }
    sleep(1);

    // 销毁互斥锁。放在sleep之后,防止主线程执行过快,销毁了正在使用的互斥锁。
    ret = pthread_mutex_destroy(&mutex);
    if (0 != ret) {
        printf("互斥锁销毁失败.\n");
        return 1;
    }
    printf("互斥锁已销毁.\n");

    return 0;
}

运行结果:符合预期

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

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

相关文章

feign远程调用原理

目录 一、简介 二、调用流程分析 2.1 添加注解 2.2 Import(FeignClientsRegistrar.class)&#xff0c; 2.3 代理创建流程 2.4 代理调用 一、简介 feign是springCloud全家桶中的远程调用组件&#xff0c;其底层主要依赖于Java的动态代理机制&#xff0c;然后基于http client…

【软件测试】全网火爆,实战Web项目前后台的bug定位(超详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 系统整体架构 Se…

【Java入门合集】第二章Java语言基础(三)

【Java入门合集】第二章Java语言基础&#xff08;三&#xff09; 博主&#xff1a;命运之光 专栏&#xff1a;JAVA入门 学习目标 掌握变量、常量、表达式的概念&#xff0c;数据类型及变量的定义方法&#xff1b; 掌握常用运算符的使用&#xff1b; 掌握程序的顺序结构、选择结…

【微信小程序开发】微信小程序集成腾讯位置项目配置

第一步 进入官网 按照Hello World流程走 腾讯位置服务官网 1、申请密钥 当然没账号的要先注册一个账号 在我的应用里创建一个新的应用&#xff0c;印象中需要小程序ID&#xff0c;去微信开发者工具里面找到自己的小程序ID填入即可 添加key中勾选勾选WebServiceAPI 2、下载S…

锐龙7000PBO温度墙设置

AMD的锐龙7000处理器首发评测大家也都看过了&#xff0c;很多人关心的都是它的性能是否可以超越12代酷睿甚至即将发布的13代酷睿&#xff0c;这方面的测试结果差不多了&#xff0c;但是很多人不知道的是散热问题更需要关注。 在评测中&#xff0c;锐龙9 7950X在拷机时温度达到…

【PCIE体系结构七】数据链路层介绍

&#x1f449;个人主页&#xff1a;highman110 &#x1f449;作者简介&#xff1a;一名硬件工程师&#xff0c;持续学习&#xff0c;不断记录&#xff0c;保持思考&#xff0c;输出干货内容 参考书籍&#xff1a;PCI_Express体系结构导读 目录 前言 数据链路层概述 数据链路层…

命名ACL配置

命名ACL配置 【实验目的】 掌握命名ACL的配置。验证配置。 【实验拓扑】 实验拓扑如图1所示。 图1 实验拓扑 设备参数如表所示。 表1 设备参数表 设备 接口 IP地址 子网掩码 默认网关 R1 S0/3/0 192.168.1.1 255.255.255.252 N/A Fa0/0 192.168.2.1 255.255.…

05_Uboot源码目录分析

目录 Uboot 源码目录分析 arch 文件夹 board 文件夹 configs 文件夹 .u-boot.xxx_cmd 文件 Makefile 文件 u-boot.xxx文件 .config文件 README Uboot 源码目录分析 学会uboot使用以后就可以尝试移uboot到自己的开发板上了,但是在移植之前需要我们得先分析一遍uboot的…

什么是Spring FactoryBean?有什么作用?

1、什么是Spring Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架&#xff0c;以 IOC和AOP为内核。含有七大核心模块 2、Spring的七大模块 (1)Spring Core&#xff1a;核心容器提供了Spring的基本功能。核心容器的核心功能是用IOC 容器来管理类的依赖关系&#xff…

卷积神经网络详解

&#xff08;一&#xff09;网络结构 一个卷积神经网络里包括5部分——输入层、若干个卷积操作和池化层结合的部分、全局平均池化层、输出层&#xff1a; ● 输入层&#xff1a;将每个像素代表一个特征节点输入进来。 ● 卷积操作部分&#xff1a;由多个滤波器组合的卷积层。 …

788. 逆序对的数量(C++和Python3)——2023.5.2打卡

文章目录 QuestionIdeasCode Question 给定一个长度为 n 的整数数列&#xff0c;请你计算数列中的逆序对的数量。 逆序对的定义如下&#xff1a;对于数列的第 i 个和第 j 个元素&#xff0c;如果满足 i<j 且 a[i]>a[j] &#xff0c;则其为一个逆序对&#xff1b;否则不…

14-6-进程间通信-信号量

前面学习了pipe,fifo,共享内存&#xff0c;信号。 本章将讲述信号量。 一、什么是信号量/信号量集&#xff1f; 1.什么是信号量 信号量是一个计数器。信号量用于实现进程间的同步和互斥。而可以取多个正整数的信号量被称为通用信号量。 对信号量的使用场景的解读 房间&#…

MyBatis学习记录

文章目录 MyBatis介绍JDBC缺点MyBatis简化MyBatis快速入门之查询user表中的所有数据1、创建user表&#xff0c;添加数据2、创建模块&#xff0c;导入坐标3、编写MyBatis核心配置文件 --> 替换连接信息&#xff0c;解决硬编码问题4、编写SQL映射文件 --> 同一管理sql语句&…

计算机网络:DNS域名解析过程

基本概念 DNS是域名系统&#xff08;Domain Name System&#xff09;的缩写&#xff0c;也是TCP/IP网络中的一个协议。在Internet上域名与IP地址之间是一一对应的&#xff0c;域名虽然便于人们记忆&#xff0c;但计算机之间只能互相认识IP地址&#xff0c;域名和IP地址之间的转…

实例解读nn.AdaptiveAvgPool2d((1, 1))

nn.AdaptiveAvgPool2d((1, 1))在PyTorch中创建一个AdaptiveAvgPool2d类的实例。该类在输入张量上执行2D自适应平均池化。 自适应平均池化是一种池化操作&#xff0c;它计算每个输入子区域的平均值并产生一个指定大小的输出张量。子区域的大小是根据输入张量的大小和输出张量的…

5年测试点工?老鸟总结功能到接口自动化测试进阶,自动化核心竞争力...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 我们来说说 功能测…

权限提升:信息收集 .(Linux系统)

权限提升&#xff1a;信息收集. 权限提升简称提权&#xff0c;由于操作系统都是多用户操作系统&#xff0c;用户之间都有权限控制&#xff0c;比如通过 Web 漏洞拿到的是 Web 进程的权限&#xff0c;往往 Web 服务都是以一个权限很低的账号启动的&#xff0c;因此通过 Webshel…

unity 性能优化之GPU和资源优化

Shader相关优化 众所周知&#xff0c;我们在unity里编写Shader使用的HLSL/CG都是高级语言&#xff0c;这是为了可以书写一套Shader兼容多个平台&#xff0c;在unity打包的时候&#xff0c;它会编译成对应平台可以运行的指令&#xff0c;而变体则是&#xff0c;根据宏生成的&am…

【英语】大学英语CET考试,翻译部分(修饰后置,定语从句,插入语,多动句,无主句)

文章目录 3大知识点与出题形式1、修饰后置&#xff08;使用介词&#xff09;2、修饰后置&#xff08;定语从句&#xff08;被逼无奈&#xff09;/which&#xff08;非限制性&#xff0c;加高级&#xff09;&#xff09;3、修饰后置&#xff08;插入语或同位语&#xff08;只有1…

【力扣-20】有效的括号

&#x1f58a;作者 : D. Star. &#x1f4d8;专栏 : 数据结构 &#x1f606;今日分享 : 夏虫不可以语冰 : 出自「庄子秋水」。原句是“井蛙不可以语于海者&#xff0c;拘于虚也&#xff1b;夏虫不可以语于冰者&#xff0c;笃于时也&#xff1b;曲士不可以语于道者&#xff0c;束…