pthread_mutex_t互斥量

静态分配的互斥量

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

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

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
加锁和解锁互斥量

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

#include <pthread.h>
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 <pthread.h>
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 <pthread.h>
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);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

barbyQAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值