Linux线程 -- 互斥锁 和 条件变量

在多线程编程中,互斥量(mutex)是用于保护共享资源的同步机制,确保在任一时刻只有一个线程能够访问共享资源。互斥量用于防止竞态条件(race conditions),确保数据一致性。

基本概念

互斥量(mutex)是一种锁机制,用于实现线程之间的互斥访问。它有两个基本操作:

  1. 锁定(lock):一个线程锁定互斥量以获得对共享资源的独占访问权。如果互斥量已经被其他线程锁定,调用该操作的线程会被阻塞,直到互斥量被解锁。
  2. 解锁(unlock):一个线程解锁互斥量,释放对共享资源的独占访问权,从而允许其他被阻塞的线程获得该资源的访问权。

在Linux中使用互斥量

在Linux中,可以使用POSIX线程库(pthread)中的互斥量。下面是一些主要函数:

  1. 初始化互斥量
    • pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
  2. 销毁互斥量
    • pthread_mutex_destroy(pthread_mutex_t *mutex);
  3. 锁定互斥量
    • pthread_mutex_lock(pthread_mutex_t *mutex);
    • pthread_mutex_trylock(pthread_mutex_t *mutex);
    • pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout);
  4. 解锁互斥量
    • pthread_mutex_unlock(pthread_mutex_t *mutex);

示例代码

以下是一个使用互斥量保护共享资源的简单示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>// 互斥锁的声明
pthread_mutex_t mutex;// 共享变量
int i = 0;// 线程函数1
void *func1(void *arg)
{static char *p = "t1 is run";pthread_mutex_lock(&mutex);  // 获取互斥锁for(i = 0; i < 5; i++) {printf("func1: %d\n", i);sleep(1);}printf("func1: i = %d\n", i);pthread_mutex_unlock(&mutex);  // 释放互斥锁pthread_exit((void *)p);  // 退出线程并返回信息
}void *func2(void *arg)
{static char *p = "t2 is run";while(1){if(i == 5){pthread_mutex_lock(&mutex);  // 获取互斥锁printf("fun2: I got mutex\n");printf("fun2=========: %d\n", i);pthread_mutex_unlock(&mutex);  // 释放互斥锁pthread_exit((void *)p);  // 退出线程并返回信息}}
}int main()
{int param = 100;pthread_t t1;pthread_t t2;// 初始化互斥锁pthread_mutex_init(&mutex, NULL);char *pret = NULL;char *pret2 = NULL;// 创建线程1int ret = pthread_create(&t1, NULL, func1, (void *)&param);if(ret == 0){printf("successfully creat a pthread1!\n");}else{printf("faild to creat a pthread\n");}// 创建线程2int ret2 = pthread_create(&t2, NULL, func2, (void *)&param);if(ret2 == 0){printf("successfully creat a pthread2!\n");}else{printf("faild to creat a pthread\n");}printf("I am main: %ld\n", (unsigned long)pthread_self());// 等待线程1和2结束pthread_join(t1, (void **)&pret);pthread_join(t2, (void **)&pret2);printf("main: t1 quit: %s\n", pret);printf("main: t2 quit: %s\n", pret2);// 销毁互斥锁pthread_mutex_destroy(&mutex);return 0;
}

运行结果:

解释

  1. 定义互斥量:使用 pthread_mutex_t 类型定义一个互斥量变量 mutex
  2. 初始化互斥量:在使用互斥量之前,需要调用 pthread_mutex_init 函数初始化互斥量。
  3. 锁定和解锁互斥量:在访问共享资源时,使用 pthread_mutex_lockpthread_mutex_unlock 函数锁定和解锁互斥量,确保每次只有一个线程能够访问共享资源。
  4. 销毁互斥量:在不再需要互斥量时,调用 pthread_mutex_destroy 函数销毁互斥量。

注意事项

  1. 避免死锁:确保每个锁定的互斥量最终会被解锁,避免死锁情况的发生。
  2. 正确初始化和销毁:在使用互斥量前一定要初始化,使用后要销毁。
  3. 最小锁定范围:尽量减少锁定的范围,只在必要的代码段中使用锁,以提高并发性。

通过正确使用互斥量,可以确保多线程程序中的共享资源被安全地访问和修改,防止竞态条件的发生。

问题1:什么情况会造成死锁?

在Linux系统中,互斥锁(mutex)用于确保多个线程不会同时访问共享资源。然而,不正确的使用互斥锁可能会导致死锁。最常见的情况如下:

相互持有资源且等待对方释放

这是最经典的死锁场景,通常被称为“循环等待”条件。假设有两个线程,分别持有锁A和锁B:

  • 线程1持有锁A,等待锁B。
  • 线程2持有锁B,等待锁A。

这种情况下,两个线程会无限期地等待对方释放锁,导致死锁。

条件变量

         条件变量(condition variable)是用于线程间协调的一种同步机制。它允许线程在特定条件不满足时等待,并在条件满足时收到通知继续执行。条件变量通常与互斥锁(mutex)结合使用,以确保对共享资源的访问是安全的。

条件变量的主要操作

  1. 初始化和销毁

    • pthread_cond_init: 初始化条件变量。
    • pthread_cond_destroy: 销毁条件变量。
  2. 等待和唤醒

    • pthread_cond_wait: 释放互斥锁并等待条件变量,直到收到信号。
    • pthread_cond_signal: 唤醒一个等待该条件变量的线程。
    • pthread_cond_broadcast: 唤醒所有等待该条件变量的线程。

使用条件变量的步骤

  1. 初始化条件变量和互斥锁
  2. 在条件不满足时等待
  3. 在条件满足时唤醒等待线程
  4. 销毁条件变量和互斥锁

相关函数介绍 

1. pthread_cond_init

功能

初始化条件变量。

原型
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
参数
  • cond: 指向需要初始化的条件变量的指针。
  • attr: 条件变量属性,如果为NULL,使用默认属性。
返回值
  • 成功返回0。
  • 失败返回错误码。

 示例

pthread_cond_t cond;
pthread_cond_init(&cond, NULL);

2. pthread_cond_destroy

功能

销毁条件变量。

原型
int pthread_cond_destroy(pthread_cond_t *cond);
参数
  • cond: 指向需要销毁的条件变量的指针。
返回值
  • 成功返回0。
  • 失败返回错误码。
示例
pthread_cond_destroy(&cond);

3. pthread_cond_wait

功能

等待条件变量。在等待期间,会释放与条件变量关联的互斥锁,并在收到信号后重新获得锁。

原型
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
参数
  • cond: 指向条件变量的指针。
  • mutex: 指向与条件变量关联的互斥锁的指针。
返回值
  • 成功返回0。
  • 失败返回错误码。

示例 

pthread_mutex_lock(&mutex);
while (condition_is_not_met) {pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);

4. pthread_cond_signal

功能

发送信号给一个等待该条件变量的线程,唤醒它。

原型
int pthread_cond_signal(pthread_cond_t *cond);
参数
  • cond: 指向条件变量的指针。

示例 

pthread_mutex_lock(&mutex);
condition_is_met = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
返回值
  • 成功返回0。
  • 失败返回错误码。

5. pthread_cond_broadcast

功能

发送信号给所有等待该条件变量的线程,唤醒它们。

原型
int pthread_cond_broadcast(pthread_cond_t *cond);
参数
  • cond: 指向条件变量的指针。
返回值
  • 成功返回0。
  • 失败返回错误码。
示例
pthread_mutex_lock(&mutex);
condition_is_met = 1;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);

条件变量使用示例

以下是一个示例程序,展示了如何使用条件变量和互斥锁同步两个线程的操作:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>// 定义互斥锁和条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;
int i = 0; // 共享资源// 线程函数1
void *func1(void *arg) {pthread_mutex_lock(&mutex);  // 加锁保护共享资源for (i = 0; i < 5; i++) {printf("func1: %d\n", i);sleep(1);  // 模拟工作}pthread_cond_signal(&cond);  // 通知等待线程条件已满足pthread_mutex_unlock(&mutex);  // 释放锁pthread_exit(NULL);  // 退出线程
}// 线程函数2
void *func2(void *arg) {pthread_mutex_lock(&mutex);  // 加锁保护共享资源while (i < 5) {  // 当条件不满足时等待pthread_cond_wait(&cond, &mutex);  // 等待条件变量的信号}printf("func2: %d\n", i);pthread_mutex_unlock(&mutex);  // 释放锁pthread_exit(NULL);  // 退出线程
}int main() {pthread_t t1, t2;// 初始化互斥锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);// 创建线程1和线程2pthread_create(&t1, NULL, func1, NULL);pthread_create(&t2, NULL, func2, NULL);// 等待线程1和线程2结束pthread_join(t1, NULL);pthread_join(t2, NULL);// 销毁互斥锁和条件变量pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}

运行结果如下: 

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

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

相关文章

python学习笔记-04

高级数据类型 一组按照顺序排列的值称为序列&#xff0c;python中存在三种内置的序列类型&#xff1a;字符串、列表和元组。序列可以支持索引和切片的操作&#xff0c;第一个索引值为0表示从左向右找&#xff0c;第一个索引值为负数表示从右找。 1.字符串操作 1.1 切片 切片…

Unity 编辑器扩展 一键替换指定物体下的所有材质球

先看效果 实现方案 1&#xff1a;创建几个用于测试的Cube 2&#xff1a;创建一个脚本 3:编写脚本内容 主要是这部分的逻辑 附上完整代码 using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine;public class Tool {[MenuItem(…

json文件操作和异常处理

目录 按行读取文件readline() 读取大文件: json文件: json文件介绍: json的语法&#xff1a; 读取json文件: json文件写入: 异常&#xff1a; 捕获异常: 捕获指定类型的异常: 捕获未知类型的异常(使用最多): 异常捕获的完整结构: 异常传递: ​编辑抛出异常: 按行…

A6500-LC LVDT 前置器,用于A6500-UM, 导轨安装

电源 22.5V to 32VDC <30mA <0.1%/V <60V( 使用SELV/PELV 供电电源) 约2.2Vrms,5kHz IP20 IEC 60529 -35C to 75C(-31F to 167F) -35C to 85C(-31F to 185F) 电流损耗 供电电压对 运行温度 存储温度 0.35mm(0.014 in ),10 to 55Hz 15g 根据 EN 60068-2-27 根据IEC 613…

linux必学基础命令大全

一切皆文件&#xff0c;每个文件都有具体的用途 命令快捷查看目录 常用命令 - 目录类1、ls 查看当前目录下的文件2、man查看命令详细信息3、pwd 查看当前目录 -4、cd 进入目录5、清屏命令6、mkdir创建目录7、du查看文件或者文件夹大小 常用命令 - 文件类1、vim/vi使用2、cat 查…

Maven实战: 从工程创建自定义archetype

在上一节中(创建自定义archetype)我们手动创建了一个项目模板&#xff0c;经过5步能创建出一个项目模板&#xff0c;如果我有一个现成的项目&#xff0c;想用这个项目作为模板来生成其他项目呢&#xff1f;Maven提供了基于项目生成archetype模板的能力&#xff0c;我们分3步来讲…

Windows下 CLion中,配置 OpenCV、LibTorch

首先按照win下C部署深度学习模型之clion配置pytorchopencv教程记录 步骤配置。 LibTorch 部分 在测试LibTorch时会出现类似 c10.dll not found 的问题&#xff08;Debug才有&#xff09;&#xff1a; 参考C部署Pytorch&#xff08;Libtorch&#xff09;出现问题、错误汇总和 …

行业分析---造车新势力之理想汽车

1 前言 在之前的博客中&#xff0c;笔者撰写了多篇行业类分析的文章&#xff08;科技新能源&#xff09;&#xff1a; 《行业分析---我眼中的Apple Inc.》 《行业分析---马斯克的Tesla》 《行业分析---造车新势力之蔚来汽车》 《行业分析---造车新势力之小鹏汽车》 此类文章的受…

vulntarget-b记录(Sliver学习)

网络环境 域控&#xff08;Win2016&#xff09; vulntarget\administrator&#xff1a;Admin123、&#xff08;首次登陆要改密码修改为Admin123&#xff09; vulntarget\win101&#xff1a;admin#123 边界Web主机突破 nmap扫描发现81端口 通过扫描&#xff0c;fuzz出来了后…

DJI FlyCart 30 天空 “吊车”概念板评测!

从消费级无人机&#xff0c;到专业级无人机&#xff0c;再到行业级无人机&#xff0c;大疆的探索脚步从未停下。现在&#xff0c;大疆已经正式进入民用运载无人机行列&#xff01;就在8月16日&#xff0c;大疆正式发布首款民用运载无人机 —— DJI FlyCart 30。 作为大疆第一代…

Flutter基础 -- Flutter基础特性

目录 1. 一切都是 widget 1.1 UI 组件&#xff01;&#xff01;&#xff01; 1.2 架构设计 1.3 sdk 源码目录 1.4 widget 分类&#xff01;&#xff01;&#xff01; 参考文献 2. devTools 调试工具 2.1 启动调试器 2.2 布局面板 参考文献 3. 布局约束规则 3.1 让子…

亲,你有多久没有清理过你电脑的 DNS 缓存了?

最近明月因为工作关系更换了几次使用的电脑,期间就发现明明另一台电脑访问某个网址是正常,换一台电脑后就会出现无法访问的现象,并且用的还是同一个宽带网络,实在是太诡异了!后来还是突然想起来 DNS 缓存这个问题,立马清除了那台电脑的 DNS 缓存后,打不开的网址顺利的呈…

FPGA PCIe PIO代码的学习

目录 背景 应用场景 代码架构分析 结论 背景 本项目是基于xinlinx官方的PCIe IP 7 series integrated block for PCI Express。根据官方的例程加上官方给的example&#xff0c;对代码进行分析。 应用场景 对一些速率要求不高的&#xff0c;比如IO操作&#xff0c;推荐使用…

对新手友好的最简单方便的本地项目关联git远程仓库教程

对新手友好的最简单方便的本地项目关联git远程仓库教程 前置条件1.本地项目2.gitee上创建同名项目 关联操作1.在本地进行clone远程仓库操作2.把本地项目下的目录和文件都复制到这个克隆自git的项目文件夹里面3.查看文件状态和提交文件 在我们创建项目时&#xff0c;一般都是在本…

QAnything-1.4.01.4.1版本更新!使用指北!

久等了各位&#xff01;时隔一个多月&#xff0c;我们在4月26日和5月20日接连发布了v1.4.0和v1.4.1两个版本&#xff0c;带来了问答性能&#xff0c;解析效果等多方面的改进&#xff0c;并新增了大量的新功能和新特性 详见&#xff1a;releases 以及 使用说明 最新特性表 开发…

13-至少有5名直接下属的经理(高频 SQL 50 题基础版)

13-至少有5名直接下属的经理 select name from Employee where id in (select managerId -- 查找大于5的经理idfrom Employeegroup by managerId -- 根据id分组having count(*)>5); -- 根据分组的数据进行求个数

java连接MySQL数据库

环境准备 JDK安装 Navicat MySQL安装 下载jar包 MySQL :: Download MySQL Connector/J (Archived Versions) 创建数据库 1. 打开Navicat 2. 新建数据库 3. 定义数据库名 4. 新建查询 运行如下代码创建表log CREATE TABLE log (user VARCHAR(255) NOT NULL UNIQUE,pwd …

未见过类型题每周总结(个人向)

1.DP40 小红取数 题目 解析 一道01背包的衍生问题&#xff0c;我们可以按照它的思路定义数组dp[i][j],表示前i个数中%k为j的最大和。为什么设置未%k的最大和呢&#xff1f;是因为当两个数分别%k&#xff0c;如a%kx&#xff0c;b%ky。那么&#xff08;ab&#xff09;%k&#…

C++基础与深度解析 | 类进阶 | 运算符重载 | 类的继承 | 虚函数

文章目录 一、运算符重载二、类的继承1.类的继承2.虚函数 一、运算符重载 在C中&#xff0c;operator关键字用于重载运算符&#xff0c;使得类的实例可以使用内置的操作符&#xff08;如、-、*、/等&#xff09;进行操作。 运算符重载的特性&#xff1a; 重载不能发明新的运算…

通信协议:常见的芯片间通信协议

相关阅读 通信协议https://blog.csdn.net/weixin_45791458/category_12452508.html?spm1001.2014.3001.5482 本文将简单介绍一些常见的芯片间通信协议&#xff0c;但不会涉及到协议的具体细节。首先说明&#xff0c;芯片间通信方式根据通信时钟的区别可以分为&#xff1a;异步…