pthread_mutex_t互斥量
admin
2024-04-04 04:48:36
0

静态分配的互斥量

互斥量既可以像静态变量那样分配,也可以在运行时动态创建(例如,通过malloc()在一块内存中分配)。这里先介绍静态分配,随后介绍动态分配。

互斥量是属于pthrad_mutex_t类型的变量。在使用之前必须对其初始化。对于静态分配的互斥量而言,可如下所示,将PTHREAD_MUTEX_INITIALIZER赋给变量:

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

加锁和解锁互斥量

初始化之后,互斥量处于未锁定状态。函数pthread_mutex_lock()可以锁定某一互斥量,而函数pthread_mutex_unlock()则可以将一个互斥量解锁。

#include 
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);Both return 0 on success, or a positive error number on error.
pthread_mutex_lock()
  • 如果互斥量当前处于未锁定状态, pthread_mutex_lock()将锁定互斥量并立即返回。
  • 如果其他线程已经锁定了这一互斥量,那么pthread_lock()会一直阻塞,直至该互斥量被解锁,调用将锁定该互斥量并返回。
  • 如果发起pthread_mutex_lock()调用的线程自身之前已然将目标互斥量锁定,对于互斥量的默认类型而言,可能会产生两种后果——视具体实现而定:线程陷入死锁(deadlock),因为试图锁定已为自己所持有的互斥量而遭到阻塞;或者调用失败,返回EDEADLK 错误。在Linux上,默认情况下线程会发生死锁。
pthread_mutex_unlock()

pthread_mutex_unlock()将解锁之前已遭调用线程锁定的互斥量,以下行为均属错误:

  • 对处于未锁定状态的互斥量进行解锁。
  • 解锁由其他线程锁定的互斥量。

如果有不止一个线程在等待获取由函数pthread_mutex_unlock()解锁的互斥量,则无法判断究竟哪个线程如愿以偿。

pthread_mutex_trylock()和pthread_mutex_timedlock()

Pthread API提供了pthread_mutex_lock()函数的两个变体:ptrhead_mutex_trylock()ptrhead_mutex_timelock()

  • 如果信号量已然锁定,对其执行函数pthread_mutex_trylock()会失败并返回EBUSY错误,除此之外,该函数与pthread_mutex_lock()行为相同。
  • 除了调用者可以指定一个附加参数abstime(设置线程等待获取互斥量时休眠的时间限制)外,pthread_mutex_timedlock()pthread_mutex_lock()没有差别。如果参数abstime指定的时间间隔期满,而调用线程又没有获得对互斥量的拥有权,那么函数pthread_mutex_timedlock()返回ETIMEDOUT错误。

函数pthread_mutex_trylock()pthread_mutex_timedlock()pthread_mutex_lock()的使用频率要低很多。在大多数经过良好设计的应用程序中,线程对互斥量的持有时间应尽可能短,以避免妨碍其他线程的并发执行。这也保证了遭阻塞的其他线程可以很快获取对互斥量的锁定,若某一线程使用pthread_mutex_trylock()周期性地轮询是否可以对互斥量加锁,则有可能要承担这样的风险:当队列中的其他线程通过调用pthread_mutex_lock()相继获得对互斥量的访问时,该线程将始终与此互斥量无缘。

动态初始化互斥量

静态初始值PTHREAD_MUTEX_INITIALIZER,只能用于对如下互斥量进行初始化:经由静态分配且携带默认属性
其他情况下,必须调用pthread_mutex_init()对互斥量进行动态初始化。

#include 
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);Returns 0 on success, or a positive error number on error.
  • 参数mutex
    • 指定函数执行初始化操作的目标互斥量。
  • 参数attr
    • 是指向pthread_mutexattr_t类型对象的指针,该对象在函数调用之前已经过了初始化处理,用于定义互斥量的属性。
    • 若将attr设为NULL,则该互斥量的各种属性会取默认值。

SUSv3规定,初始化一个业已初始化的互斥量将导致未定义的行为,应当避免这一行为。
以下情况必须使用pthread_mutex_init()而非静态初始化互斥量。

  • 动态分配于堆中的互斥量。
    • 例如,动态创建针对某一结构的链表,表中每个结构都包含一个pthread_mutex_t类型的字段来存放互斥量,借以保护对该结构的访问。
  • 互斥量是在栈中分配的自动变量。
  • 初始化经由静态分配,且不使用默认属性的互斥量。

(也即是,只有静态分配且使用默认属性的互斥量才用静态初始化。)

当不再需要经由自动或动态分配的互斥量时,应使用pthread_mutex_destroy()将其销毁。(对于使用PHREAD_MUTEX_INTIALIZER 静态初始化的互斥量,无需调用pthread_mutex_destroy()。)

#include 
int pthread_mutex_destroy(pthread_mutex_t * mutex);Returns 0 on success, or a positive error number on error.

只有互斥量处于未锁定状态,且后续也无任何线程企图锁定它时,将其销毁才是安全的。
若互斥量驻留于动态分配的一片内存区域中,应在释放(free)此内存区域前将其销毁。对于自动分配的互斥量,也应在宿主函数返回前将其销毁。

经由pthread_mutex_destroy()销毁的互斥量,可调用pthread_mutex_init()对其重新初始化。

互斥量类型

前面的规则归纳如下:

  • 同一线程不应对同一互斥量加锁两次。
  • 线程不应对不为自己所拥有的互斥量解锁(亦即,尚未锁定互斥量)。
  • 线程不应对一尚未锁定的互斥量做解锁动作。

准确地说,上述情况的结果取决于互斥量类型(type),SUSv3定义了以下互斥量类型:

  • PTHREAD_MUTEX_NORMAL
    • 该类型的互斥量不具有死锁检测(自检)功能。
    • 如线程试图对已由自己锁定的互斥量加锁,则发生死锁。
    • 互斥量处于未锁定状态,或者已由其他线程锁定,对其解锁会导致不确定的结果。(在Linux上,对这类互斥量的上述两种操作都会成功。
  • PTHREAD_MUTEX_ERRORCHECK
    • 对此类互斥量的所有操作都会执行错误检查。所有上述3种情况都会导致相关Pthreads函数返回错误。
    • 这类互斥量运行起来比一般类型要慢,不过可以将其作为调试工具,以发现程序哪里违反了互斥量使用的基本原则。
  • PTHREAD_MUTEX_RECURSIVE
    • 递归互斥量有一个锁计数器。
    • 当线程第一次取得互斥量时,会将锁计数器都置1.
    • 后续由同一线程执行的每次加锁操作会递增锁计数器的数值,而解锁操作则递减计数器计数。
    • 只有当锁计数器值降至0时,才会释放(release,亦即可谓其他线程所用)该互斥量。
    • 解锁时如目标互斥量处于未锁定状态,或是已由其他线程锁定,操作都会失败。

Linux的线程实现针对以上各种类型的互斥量提供了非标准的静态初始值(例如,PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP),以便对那些通过静态分配的互斥量进行初始化,而无需使用pthread_mutex_init()函数。不过,为保证程序可移植性,应该避免使用这些初始值。

除了上述类型,SUSv3还定义了PTHREAD_MUTEX_DEFAULT类型:

  • 使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量,或是经调用参数 attr为NULL的pthread_mutex_init()函数所创建的互斥量,都属于此类型。
  • Linux上,PTHREAD_MUTEX_DEFAULT类型互斥量的行为与PTHREAD_MUTEX_NORMAL类型相仿。

下例创建一个带有错误检查属性的互斥量:

pthread_mutex_t mtx;
pthread_mutexattr_t mtxAttr;
pthread_mutexattr_init(&mtxAttr);
pthread_mutexattr_settype(&mtxAttr, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(mtx, &mtxAttr);
pthread_mutexattr_destroy(&mtxAttr);

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...