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

pthread_mutex_unlock函数的概念和用法

pthread_mutex_unlock 是 POSIX 线程库(pthreads)中一个核心函数,用于释放(解锁)一个之前由调用线程锁定的互斥锁(mutex)。它的主要目的是在访问完共享资源后,允许其他线程有机会获取该锁并访问资源。

核心概念

在这里插入图片描述

  1. 互斥锁 (Mutex):

    • 一种同步原语,用于保护共享资源(如全局变量、数据结构、文件等)免受并发访问的破坏。
    • 基本思想:在访问共享资源前,线程必须锁定pthread_mutex_lock)与该资源关联的互斥锁。如果锁已被其他线程持有,则尝试加锁的线程会阻塞(等待)直到锁变得可用。
    • 当一个线程持有锁时,它获得了对共享资源的独占访问权。其他试图获取同一锁的线程将被阻塞。
  2. 解锁 (Unlocking):

    • 当持有锁的线程完成对共享资源的操作后,它必须调用 pthread_mutex_unlock 来释放该锁。
    • 这是关键步骤。如果不解锁:
      • 其他等待该锁的线程将永远阻塞(死锁)。
      • 共享资源将无法被其他线程访问。

pthread_mutex_unlock 函数原型

#include <pthread.h>int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 参数:
    • mutex: 指向一个已初始化的 pthread_mutex_t 类型变量的指针,代表你要解锁的那个互斥锁。
  • 返回值:
    • 成功时返回 0
    • 失败时返回一个非零的错误码(例如 EINVAL - 无效的 mutex, EPERM - 当前线程不持有该锁)。

关键用法和规则

  1. 配对使用: pthread_mutex_unlock 必须pthread_mutex_lock(或其变体如 pthread_mutex_trylock, pthread_mutex_timedlock) 配对使用。每次成功的加锁操作后,最终必须有一次对应的解锁操作。
  2. 谁加锁,谁解锁: 通常(对于 PTHREAD_MUTEX_DEFAULTPTHREAD_MUTEX_NORMAL 类型的锁),解锁的线程必须是之前锁定该互斥锁的同一个线程。试图由另一个线程解锁会导致未定义行为(通常是错误 EPERM)。
  3. 作用域: 解锁操作应放在访问共享资源的临界区代码段之后,且应确保在所有退出路径(包括通过 return, break, continue, goto 或异常抛出)上都能执行到解锁操作。通常使用 lock/unlock 包裹临界区代码。
  4. 错误处理: 虽然在实际编程中解锁失败相对少见(通常意味着逻辑错误),但检查返回值是良好的实践,尤其是在调试阶段。
  5. 解锁未锁定的锁: 尝试解锁一个未被任何线程锁定的锁(或已经被解锁的锁)的行为取决于互斥锁的类型属性:
    • PTHREAD_MUTEX_DEFAULT/NORMAL: 未定义行为(可能导致程序崩溃或错误 EPERM)。
    • PTHREAD_MUTEX_ERRORCHECK: 会明确返回错误 EPERM
    • PTHREAD_MUTEX_RECURSIVE: 减少锁计数,只有计数减到 0 时才真正释放锁。解锁未锁定的递归锁同样错误。
    • PTHREAD_MUTEX_ADAPTIVE: 通常类似 NORMAL,未定义。

简单示例

#include <pthread.h>
#include <stdio.h>// 共享资源
int counter = 0;// 保护 counter 的互斥锁
pthread_mutex_t counter_lock = PTHREAD_MUTEX_INITIALIZER;void *increment_counter(void *arg) {for (int i = 0; i < 100000; ++i) {// 进入临界区前加锁if (pthread_mutex_lock(&counter_lock) != 0) {perror("pthread_mutex_lock failed");return NULL;}// 临界区开始 - 安全地访问共享资源 countercounter++;// 临界区结束// 访问完成后解锁if (pthread_mutex_unlock(&counter_lock) != 0) {perror("pthread_mutex_unlock failed");return NULL;}}return NULL;
}int main() {pthread_t thread1, thread2;// 创建两个线程并发执行 increment_counterpthread_create(&thread1, NULL, increment_counter, NULL);pthread_create(&thread2, NULL, increment_counter, NULL);// 等待线程结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);// 打印最终结果 (应为 200000)printf("Final counter value: %d\n", counter);return 0;
}

解释示例中的解锁 (pthread_mutex_unlock)

  1. 线程调用 pthread_mutex_lock(&counter_lock) 尝试获取锁。如果锁空闲,它获得锁并进入临界区;如果锁被另一个线程持有,它会阻塞等待。
  2. 线程在临界区内安全地修改 counter
  3. 线程完成临界区操作后,立即调用 pthread_mutex_unlock(&counter_lock)。这一步至关重要
    • 它释放了 counter_lock
    • 它允许其他正在等待该锁的线程(本例中可能是另一个 increment_counter 线程)被唤醒并有机会获取锁,进入它们自己的临界区。
  4. 如果没有 pthread_mutex_unlock 的调用,第一个获得锁的线程将永远持有锁。第二个线程将在 pthread_mutex_lock 处永久阻塞,程序将死锁。最终 counter 的值只会是 100000(只有第一个线程完成),而不是预期的 200000。

总结

pthread_mutex_unlock 是使用 POSIX 互斥锁进行多线程同步的必要组成部分。它的职责是释放锁,从而结束当前线程对共享资源的独占访问,并允许其他等待线程获取锁并继续执行。务必牢记解锁操作与加锁操作严格配对、在正确的位置(临界区之后)调用,并且避免由错误的线程解锁。正确使用锁和解锁是防止多线程程序中数据竞争和死锁的关键。

http://www.dtcms.com/a/278056.html

相关文章:

  • 大规模电商系统分库分表实战经验分享
  • NFSV4锁机制(三)
  • 编程技术杂谈2.0
  • DVWA靶场通关笔记-XSS DOM(High级别)
  • 垃圾收集器-Serial Old
  • CVE-2022-0609
  • vue2入门(1)vue核心语法详解复习笔记
  • 【开源项目】网络诊断告别命令行!NetSonar:开源多协议网络诊断利器
  • 1.1.1+1.1.3 操作系统的概念、功能
  • c++无锁队列moodycamel::ConcurrentQueue测试结果
  • 在高并发场景下,仅依赖数据库机制(如行锁、版本控制)无法完全避免数据异常的问题
  • Sping AI Alibaba
  • 第11章 AB实验评估指标体系
  • Soul方程式:Z世代背景下兴趣社交平台的商业模式解析
  • Java行业前景如何?零基础又该如何去学Java?
  • 深入理解 RocketMQ:生产者详解
  • 并行并发丨C++ 协程、现场池 学习笔记
  • 闲庭信步使用图像验证平台加速FPGA的开发:第十三课——图像浮雕效果的FPGA实现
  • 语言模型常用的激活函数(Sigmoid ,GeLU ,SwiGLU,GLU,SiLU,Swish)
  • 算法-汽水瓶兑换
  • Spring AI 项目实战(十七):Spring Boot + AI + 通义千问星辰航空智能机票预订系统(附完整源码)
  • 【webrtc】gcc当前可用码率3:x264响应码率改变
  • 系规备考论文:论IT服务部署实施方法
  • 西藏氆氇新生:牦牛绒混搭液态金属的先锋尝试
  • 分布式锁踩坑记:当“防重“变成了“重复“
  • JAVA并发——什么是Java的原子性、可见性和有序性
  • Redis缓存设计与性能优化指南
  • 使用Starrocks替换Clickhouse的理由
  • C++封装、多态、继承
  • 在 Ubuntu 下安装 MySQL 数据库