linux c 递归锁的介绍
递归锁的递归特性确实只是对于持有锁的线程。当一个线程获取了递归锁后,它可以多次重复获取该锁,而不会导致自身阻塞或死锁。这是递归锁的重要特点,它允许同一个线程在已经持有锁的情况下,再次获取相同的锁。
然而,对于其他线程,如果该递归锁已经被某个线程持有(即使是持有锁的线程自身多次获取的),那么其他线程在尝试获取该锁时,仍然会被阻塞,直到持有锁的线程进行了相应次数的解锁操作,使得锁完全释放后,其他线程才有机会获取该锁。
递归锁通常应用于递归函数或方法,以及存在嵌套临界区的情况。在这些场景下,同一个线程可能需要在不同的递归层次或嵌套的临界区中多次获取同一个锁。通过使用递归锁,可以确保线程不会因为自身重复获取锁而产生死锁问题。
需要注意的是,使用递归锁时,持有锁的线程必须进行相应次数的解锁操作,以平衡之前的加锁操作。也就是说,对于每次加锁,都需要有一个对应的解锁,只有当解锁次数与加锁次数相等时,锁才会完全释放,其他线程才能获取该锁。如果解锁次数不足或过多,都可能导致死锁或其他同步问题的发生。
如在一个递归函数中,每次递归调用都需要获取相同的锁进行保护,递归锁就可以满足这种需求。而如果使用普通的互斥锁,在同一线程中再次尝试获取已经持有的锁时,就会导致死锁。
PTHREAD_MUTEX_RECURSIVE 是一种递归锁,它允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。
使用 PTHREAD_MUTEX_RECURSIVE 递归锁的一般步骤如下:
- 包含必要的头文件:
#include <pthread.h>。 - 初始化互斥锁属性对象:使用
pthread_mutexattr_init(&attr);初始化一个互斥锁属性对象attr。 - 设置锁的类型为递归锁:通过
pthread_mutexattr_settype(&attr, pthread_mutex_recursive);将互斥锁的类型设置为递归锁。 - 初始化递归锁:使用
pthread_mutex_init(&mutex, &attr);初始化递归锁mutex,并关联之前设置好属性的attr对象。 - 在需要加锁的代码段前加锁:通过
pthread_mutex_lock(&mutex);进行加锁操作。如果是在同一个线程中且之前已经加过锁,不会产生死锁,而是可以成功再次加锁。 - 在相应的代码段结束后解锁:调用
pthread_mutex_unlock(&mutex);释放锁。需要注意的是,加锁几次就需要解锁几次,才能完全释放该锁,以便其他线程获取该锁。 - 不再使用递归锁后,销毁互斥锁:使用
pthread_mutex_destroy(&mutex);释放锁资源。
下面是一个简单的示例代码,演示了递归锁的使用:
#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>pthread_mutex_t mutex;
pthread_mutexattr_t attr;void*func(void*arg){if(*(char*)arg=='\0') return NULL;pthread_mutex_lock(&mutex); // 加锁char*str = (char*)arg;while(*str!='\0'){fputc(*str, stdout);str++;}fputc('\n', stdout);func((char*)arg + 1); // 递归调用自身,再次加锁pthread_mutex_unlock(&mutex); // 解锁return NULL;
}int main()
{int ret;if((ret=pthread_mutexattr_init(&attr))!=0){ // 初始化互斥锁属性对象fprintf(stderr, "create mutex attribute error.msg:%s", strerror(ret));exit(1);}pthread_mutexattr_settype(&attr, pthread_mutex_recursive); // 设置为递归锁属性pthread_mutex_init(&mutex, &attr); // 初始化递归锁pthread_t p1, p2;char str1[8], str2[8];sprintf(str1, "abcdefg");sprintf(str2, "1234567");if((ret=pthread_create(&p1, NULL, func, str1))!=0){ // 创建线程 p1 并执行 func 函数fprintf(stderr, "create thread error.msg:%s", strerror(ret));exit(1);}if((ret=pthread_create(&p2, NULL, func, str2))!=0){ // 创建线程 p2 并执行 func 函数fprintf(stderr, "create thread error.msg:%s", strerror(ret));exit(1);}pthread_join(p1, NULL); // 等待线程 p1 结束pthread_join(p2, NULL); // 等待线程 p2 结束
}
在上述示例中,func 函数中存在递归调用,并且在递归调用时会再次对同一个锁进行加锁操作。如果使用普通的互斥锁(非递归锁),则会导致死锁。而使用 PTHREAD_MUTEX_RECURSIVE 递归锁,就可以在同一个线程中多次加锁而不会产生死锁。
需要注意的是,虽然递归锁提供了方便,但也应该谨慎使用,尽量避免在不必要的情况下过度使用递归锁,因为它可能会导致一些难以察觉的逻辑错误或性能问题。在实际编程中,确保正确地管理锁的获取和释放次数,以避免出现意外的情况。另外,在使用完递归锁后,记得使用 pthread_mutex_destroy 函数销毁锁,以释放相关资源。
在POSIX标准中,递归互斥锁的类型通常被定义为PTHREAD_MUTEX_RECURSIVE。但是在一些GNU系统(如Linux),PTHREAD_MUTEX_RECURSIVE常被定义为PTHREAD_MUTEX_RECURSIVE_NP,其中NP代表“Non-Portable”(非可移植的)。
具体来说:
PTHREAD_MUTEX_RECURSIVE:POSIX标准定义的递归互斥锁类型。这在POSIX兼容的系统中应该可以直接使用。PTHREAD_MUTEX_RECURSIVE_NP:GNU特定的递归互斥锁类型。在一些GNU系统中,这个宏定义被用来替代标准的PTHREAD_MUTEX_RECURSIVE。
通常,POSIX兼容系统(如现代的Linux发行版)应该支持PTHREAD_MUTEX_RECURSIVE。但是,如果你的系统使用了较旧的或特定的GNU库版本,你可能需要使用PTHREAD_MUTEX_RECURSIVE_NP。
检查和使用合适的宏
为了编写兼容性更好的代码,可以在编译时检查是否定义了PTHREAD_MUTEX_RECURSIVE,并根据需要使用适当的宏。例如:
#include <pthread.h>int main() {pthread_mutexattr_t attr;pthread_mutexattr_init(&attr);#ifdef PTHREAD_MUTEX_RECURSIVEpthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
#elsepthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
#endif// 其余代码...pthread_mutexattr_destroy(&attr);return 0;
}
定义_GNU_SOURCE
在包含<pthread.h>之前定义_GNU_SOURCE宏也可以启用所有GNU扩展功能,确保PTHREAD_MUTEX_RECURSIVE宏被正确识别:
#define _GNU_SOURCE
#include <pthread.h>// 其余代码...
相关文章:
linux c 递归锁的介绍
递归锁的递归特性确实只是对于持有锁的线程。当一个线程获取了递归锁后,它可以多次重复获取该锁,而不会导致自身阻塞或死锁。这是递归锁的重要特点,它允许同一个线程在已经持有锁的情况下,再次获取相同的锁。 然而,对…...
React好用的组件库有哪些
React好用的组件库有很多,它们各自具有不同的特点和优势,适用于不同的开发场景和需求。以下是一些受欢迎的React组件库及其特点: Material-UI(现更名为MUI) 特点:这是一个开源的React组件库,实…...
简单快捷!Yarn的安装与使用指南
Yarn 是由 Facebook (现 Meta) 开发的包管理工具。 今天,我将介绍如何使用 Yarn。 目录 Yarn 的官方网站 关于安装 版本确认 开始一个新项目(创建 package.json 文件) 安装软件包 升级包 运行脚本 执行包的命令 卸载包 总结 Yarn 的…...
【Django】前端技术-网页样式表CSS
文章目录 一、申明规则CSS的导入方式行内样式内部样式外部样式 二、CSS的选择器1. 基本选择器标签选择器: 选择一类标签 标签{}类选择器 class: 选择所有class属性一致的表情,跨标签.类名{}ID选择器:全局唯一 #id名{} 2.层次选择器…...
openssl req 详解
一、openssl req 该命令用于创建和处理PKCS#10格式的证书请求(certificate requests CSRs),也可以用来创建自签名证书( self-signed certificates)来当作根证书(root CAs)使用 -new 该选项用来…...
mysql各种锁总结
mysql全局锁 读锁(共享锁) 阻止其他用户更新,但允许他们读取数据。 写锁(排他锁) 阻止其他用户读取和更新数据。 全局锁场景:进行数据库备份 数据库备份 背景:备份数据肯定要保证数据一致…...
SpringSecurity--DelegatingFilterProxy工作流程
什么是 DelegatingFilterProxy? DelegatingFilterProxy 是 Spring 提供的一个特殊的过滤器,它起到了桥梁的作用,可以让你在 Spring 容器中管理 Servlet 容器中的过滤器。 为什么需要 DelegatingFilterProxy? 通常情况下&#x…...
GitHub每日最火火火项目(7.27)
1. 项目名称:meta - llama / llama3 项目介绍:这是 Meta Llama 3 的官方 GitHub 站点。目前尚不清楚该项目的具体功能和特点,但从名称推测,可能与 Llama 3 模型相关,或许涉及到模型的开发、训练或应用等方面。 项目地…...
git 学习总结
文章目录 一、 git 基础操作1、工作区2、暂存区3、本地仓库4、远程仓库 二、git 的本质三、分支git 命令总结 作者: baron 一、 git 基础操作 如图所示 git 总共有几个区域 工作区, 暂存区, 本地仓库, 远程仓库. 1、工作区 存放项目代码的地方,他有两种状态 Unm…...
《如何找到自己想做的事》
Arouse Enthusiasm, Give Scope to Skill, Explore The Essence *摘其两纸 我喜欢打篮球,并不是我真的喜欢这项运动,而是我喜欢团队竞技。我喜欢看书,并不是我真喜欢阅读,而是我想要了解世界运行逻辑。寻找热爱,探寻本…...
Vue中el的两种写法
大家好我是前端寄术区博主PleaSure乐事。今天了解到了Vue当中有关el的两种写法,记录下来与大家分享,希望对大家有所帮助。 方法一 解释 第一种方法我们直接用new创建并初始化一个新的 Vue 实例,并定义了 Vue 实例的数据对象,在给…...
ELK安装(Elasticsearch+Logstash+Kibana+Filebeat)
一、简介 1.1、软件简介 ELK其实是Elasticsearch,Logstash 和 Kibana三个产品的首字母缩写,这三款都是开源产品。 1.1.1、Elasticsearch简介 Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它能很方便的使大量数据具有搜索、分析…...
VScode使用Github Copilot插件时出现read ECONNREST问题的解决方法
文章目录 read ECONNREST查看是否仍是 Copilot 会员查看控制台输出网络连接问题浏览器设置问题笔者的话 read ECONNREST 最近使用 Copilot 时一直出现 read ECONNREST 问题,这个表示连接被对方重置了,就是说在读取数据时连接被关闭。 我首先怀疑是不是…...
充电桩浪涌保护方案—保障充电设施安全稳定运行的关键
在当今新能源汽车蓬勃发展的时代,充电桩作为电动汽车的“加油站”,其重要性不言而喻。然而,由于其复杂的电气环境和暴露于户外的特点,充电桩容易受到浪涌的影响。浪涌可能来自雷电、电网故障、大功率设备的启停等,对充…...
Python包管理工具pip
1、安装pip cmd管理员模式打开控制台 python -m pip install --upgrade pip 2、添加pip环境变量 pip 路径 C:\Users\1\AppData\Local\Programs\Python\Python312\Scripts...
最全国内13家DNS分享 解决网页被恶意跳转或无法打开问题
腾讯 DNS (DNSPod) 腾讯 DNS 是由 DNSPod 提供的公共免费 DNS 服务。DNSPod 已被腾讯收购,现在属于腾讯公司所有。该 DNS 服务稳定性和连通性良好,经测试在海外也可以使用。 DNSPod 提供了 IPv4、IPv6 DNS 和 DoT/DoH 服务。 IPv4 地址: 119.29.29.29…...
最新站长工具箱源码,拥有几百个功能,安装教程
最新站长工具箱源码,拥有几百个功能,安装教程 在 Docker 上运行 docker run -e LAFREGIONCN -e APPLANGzh_CN --name my-miaoda -v ~/.miaoda-docker:/root/.miaoda -d -p 0.0.0.0:39899:39899 codegentoolbox/laftools-linux-x64:latestNOTE: 默认端…...
【算法/训练】:动态规划(线性DP)
一、路径类 1. 字母收集 思路: 1、预处理 对输入的字符矩阵我们按照要求将其转换为数字分数,由于只能往下和往右走,因此走到(i,j)的位置要就是从(i - 1, j)往下走&#…...
计算巨头 Azure、AWS 和 GCP 的比较
云计算领域由三大主要参与者主导:Microsoft Azure、Amazon Web Services (AWS) 和 Google Cloud Platform (GCP)。每个平台都为希望利用云提供基础设施、平台服务等的企业提供强大的功能。在本文中,我们将深入探讨这些平台之间的差异,重点关注…...
Thinkphp5跨域问题常见的处理方法
在ThinkPHP5中,处理跨域问题通常涉及配置中间件或直接在控制器中设置响应头。以下是几种常见的解决跨域问题的方法: 1. 使用中间件处理跨域 你可以创建一个中间件来专门处理跨域请求。这个中间件会检查请求的来源,并设置相应的响应头来允许…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
