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

linux c 递归锁的介绍

递归锁的递归特性确实只是对于持有锁的线程。当一个线程获取了递归锁后,它可以多次重复获取该锁,而不会导致自身阻塞或死锁。这是递归锁的重要特点,它允许同一个线程在已经持有锁的情况下,再次获取相同的锁。

然而,对于其他线程,如果该递归锁已经被某个线程持有(即使是持有锁的线程自身多次获取的),那么其他线程在尝试获取该锁时,仍然会被阻塞,直到持有锁的线程进行了相应次数的解锁操作,使得锁完全释放后,其他线程才有机会获取该锁。

递归锁通常应用于递归函数或方法,以及存在嵌套临界区的情况。在这些场景下,同一个线程可能需要在不同的递归层次或嵌套的临界区中多次获取同一个锁。通过使用递归锁,可以确保线程不会因为自身重复获取锁而产生死锁问题。

需要注意的是,使用递归锁时,持有锁的线程必须进行相应次数的解锁操作,以平衡之前的加锁操作。也就是说,对于每次加锁,都需要有一个对应的解锁,只有当解锁次数与加锁次数相等时,锁才会完全释放,其他线程才能获取该锁。如果解锁次数不足或过多,都可能导致死锁或其他同步问题的发生。

如在一个递归函数中,每次递归调用都需要获取相同的锁进行保护,递归锁就可以满足这种需求。而如果使用普通的互斥锁,在同一线程中再次尝试获取已经持有的锁时,就会导致死锁。

PTHREAD_MUTEX_RECURSIVE 是一种递归锁,它允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。

使用 PTHREAD_MUTEX_RECURSIVE 递归锁的一般步骤如下:

  1. 包含必要的头文件:#include <pthread.h>
  2. 初始化互斥锁属性对象:使用 pthread_mutexattr_init(&attr); 初始化一个互斥锁属性对象 attr
  3. 设置锁的类型为递归锁:通过 pthread_mutexattr_settype(&attr, pthread_mutex_recursive); 将互斥锁的类型设置为递归锁。
  4. 初始化递归锁:使用 pthread_mutex_init(&mutex, &attr); 初始化递归锁 mutex,并关联之前设置好属性的 attr 对象。
  5. 在需要加锁的代码段前加锁:通过 pthread_mutex_lock(&mutex); 进行加锁操作。如果是在同一个线程中且之前已经加过锁,不会产生死锁,而是可以成功再次加锁。
  6. 在相应的代码段结束后解锁:调用 pthread_mutex_unlock(&mutex); 释放锁。需要注意的是,加锁几次就需要解锁几次,才能完全释放该锁,以便其他线程获取该锁。
  7. 不再使用递归锁后,销毁互斥锁:使用 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 递归锁的介绍

递归锁的递归特性确实只是对于持有锁的线程。当一个线程获取了递归锁后&#xff0c;它可以多次重复获取该锁&#xff0c;而不会导致自身阻塞或死锁。这是递归锁的重要特点&#xff0c;它允许同一个线程在已经持有锁的情况下&#xff0c;再次获取相同的锁。 然而&#xff0c;对…...

React好用的组件库有哪些

React好用的组件库有很多&#xff0c;它们各自具有不同的特点和优势&#xff0c;适用于不同的开发场景和需求。以下是一些受欢迎的React组件库及其特点&#xff1a; Material-UI&#xff08;现更名为MUI&#xff09; 特点&#xff1a;这是一个开源的React组件库&#xff0c;实…...

简单快捷!Yarn的安装与使用指南

Yarn 是由 Facebook (现 Meta) 开发的包管理工具。 今天&#xff0c;我将介绍如何使用 Yarn。 目录 Yarn 的官方网站 关于安装 版本确认 开始一个新项目&#xff08;创建 package.json 文件&#xff09; 安装软件包 升级包 运行脚本 执行包的命令 卸载包 总结 Yarn 的…...

【Django】前端技术-网页样式表CSS

文章目录 一、申明规则CSS的导入方式行内样式内部样式外部样式 二、CSS的选择器1. 基本选择器标签选择器&#xff1a; 选择一类标签 标签{}类选择器 class&#xff1a; 选择所有class属性一致的表情&#xff0c;跨标签.类名{}ID选择器&#xff1a;全局唯一 #id名{} 2.层次选择器…...

openssl req 详解

一、openssl req 该命令用于创建和处理PKCS#10格式的证书请求&#xff08;certificate requests CSRs&#xff09;&#xff0c;也可以用来创建自签名证书&#xff08; self-signed certificates&#xff09;来当作根证书&#xff08;root CAs&#xff09;使用 -new 该选项用来…...

mysql各种锁总结

mysql全局锁 读锁&#xff08;共享锁&#xff09; 阻止其他用户更新&#xff0c;但允许他们读取数据。 写锁&#xff08;排他锁&#xff09; 阻止其他用户读取和更新数据。 全局锁场景&#xff1a;进行数据库备份 数据库备份 背景&#xff1a;备份数据肯定要保证数据一致…...

SpringSecurity--DelegatingFilterProxy工作流程

什么是 DelegatingFilterProxy&#xff1f; DelegatingFilterProxy 是 Spring 提供的一个特殊的过滤器&#xff0c;它起到了桥梁的作用&#xff0c;可以让你在 Spring 容器中管理 Servlet 容器中的过滤器。 为什么需要 DelegatingFilterProxy&#xff1f; 通常情况下&#x…...

GitHub每日最火火火项目(7.27)

1. 项目名称&#xff1a;meta - llama / llama3 项目介绍&#xff1a;这是 Meta Llama 3 的官方 GitHub 站点。目前尚不清楚该项目的具体功能和特点&#xff0c;但从名称推测&#xff0c;可能与 Llama 3 模型相关&#xff0c;或许涉及到模型的开发、训练或应用等方面。 项目地…...

git 学习总结

文章目录 一、 git 基础操作1、工作区2、暂存区3、本地仓库4、远程仓库 二、git 的本质三、分支git 命令总结 作者: baron 一、 git 基础操作 如图所示 git 总共有几个区域 工作区, 暂存区, 本地仓库, 远程仓库. 1、工作区 存放项目代码的地方&#xff0c;他有两种状态 Unm…...

《如何找到自己想做的事》

Arouse Enthusiasm, Give Scope to Skill, Explore The Essence *摘其两纸 我喜欢打篮球&#xff0c;并不是我真的喜欢这项运动&#xff0c;而是我喜欢团队竞技。我喜欢看书&#xff0c;并不是我真喜欢阅读&#xff0c;而是我想要了解世界运行逻辑。寻找热爱&#xff0c;探寻本…...

Vue中el的两种写法

大家好我是前端寄术区博主PleaSure乐事。今天了解到了Vue当中有关el的两种写法&#xff0c;记录下来与大家分享&#xff0c;希望对大家有所帮助。 方法一 解释 第一种方法我们直接用new创建并初始化一个新的 Vue 实例&#xff0c;并定义了 Vue 实例的数据对象&#xff0c;在给…...

ELK安装(Elasticsearch+Logstash+Kibana+Filebeat)

一、简介 1.1、软件简介 ELK其实是Elasticsearch&#xff0c;Logstash 和 Kibana三个产品的首字母缩写&#xff0c;这三款都是开源产品。 1.1.1、Elasticsearch简介 Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它能很方便的使大量数据具有搜索、分析…...

VScode使用Github Copilot插件时出现read ECONNREST问题的解决方法

文章目录 read ECONNREST查看是否仍是 Copilot 会员查看控制台输出网络连接问题浏览器设置问题笔者的话 read ECONNREST 最近使用 Copilot 时一直出现 read ECONNREST 问题&#xff0c;这个表示连接被对方重置了&#xff0c;就是说在读取数据时连接被关闭。 我首先怀疑是不是…...

充电桩浪涌保护方案—保障充电设施安全稳定运行的关键

在当今新能源汽车蓬勃发展的时代&#xff0c;充电桩作为电动汽车的“加油站”&#xff0c;其重要性不言而喻。然而&#xff0c;由于其复杂的电气环境和暴露于户外的特点&#xff0c;充电桩容易受到浪涌的影响。浪涌可能来自雷电、电网故障、大功率设备的启停等&#xff0c;对充…...

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 已被腾讯收购&#xff0c;现在属于腾讯公司所有。该 DNS 服务稳定性和连通性良好&#xff0c;经测试在海外也可以使用。 DNSPod 提供了 IPv4、IPv6 DNS 和 DoT/DoH 服务。 IPv4 地址: 119.29.29.29…...

最新站长工具箱源码,拥有几百个功能,安装教程

最新站长工具箱源码&#xff0c;拥有几百个功能&#xff0c;安装教程 在 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. 字母收集 思路&#xff1a; 1、预处理 对输入的字符矩阵我们按照要求将其转换为数字分数&#xff0c;由于只能往下和往右走&#xff0c;因此走到&#xff08;i&#xff0c;j&#xff09;的位置要就是从&#xff08;i - 1&#xff0c; j&#xff09;往下走&#…...

计算巨头 Azure、AWS 和 GCP 的比较

云计算领域由三大主要参与者主导&#xff1a;Microsoft Azure、Amazon Web Services (AWS) 和 Google Cloud Platform (GCP)。每个平台都为希望利用云提供基础设施、平台服务等的企业提供强大的功能。在本文中&#xff0c;我们将深入探讨这些平台之间的差异&#xff0c;重点关注…...

Thinkphp5跨域问题常见的处理方法

在ThinkPHP5中&#xff0c;处理跨域问题通常涉及配置中间件或直接在控制器中设置响应头。以下是几种常见的解决跨域问题的方法&#xff1a; 1. 使用中间件处理跨域 你可以创建一个中间件来专门处理跨域请求。这个中间件会检查请求的来源&#xff0c;并设置相应的响应头来允许…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

Android屏幕刷新率与FPS(Frames Per Second) 120hz

Android屏幕刷新率与FPS(Frames Per Second) 120hz 屏幕刷新率是屏幕每秒钟刷新显示内容的次数&#xff0c;单位是赫兹&#xff08;Hz&#xff09;。 60Hz 屏幕&#xff1a;每秒刷新 60 次&#xff0c;每次刷新间隔约 16.67ms 90Hz 屏幕&#xff1a;每秒刷新 90 次&#xff0c;…...

react菜单,动态绑定点击事件,菜单分离出去单独的js文件,Ant框架

1、菜单文件treeTop.js // 顶部菜单 import { AppstoreOutlined, SettingOutlined } from ant-design/icons; // 定义菜单项数据 const treeTop [{label: Docker管理,key: 1,icon: <AppstoreOutlined />,url:"/docker/index"},{label: 权限管理,key: 2,icon:…...

C++11 constexpr和字面类型:从入门到精通

文章目录 引言一、constexpr的基本概念与使用1.1 constexpr的定义与作用1.2 constexpr变量1.3 constexpr函数1.4 constexpr在类构造函数中的应用1.5 constexpr的优势 二、字面类型的基本概念与使用2.1 字面类型的定义与作用2.2 字面类型的应用场景2.2.1 常量定义2.2.2 模板参数…...

【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器

从本章节开始&#xff0c;进入到函数有多个参数的情况&#xff0c;前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参&#xff0c;ECX是整型的第一个参数的寄存器&#xff0c;那么多个参数的情况下函数如何传参&#xff0c;下面展开介绍参数为整型时候的几种情…...

Jmeter(四) - 如何在jmeter中创建网络测试计划

1.简介 如何创建基本的 测试计划来测试网站。您将创建五个用户&#xff0c;这些用户将请求发送到JMeter网站上的两个页面。另外&#xff0c;您将告诉用户两次运行测试。 因此&#xff0c;请求总数为&#xff08;5个用户&#xff09;x&#xff08;2个请求&#xff09;x&#xff…...