linux pthread_mutex_lock(一)解决锁竞争导致优先级反转问题

1优先级反转

竞争锁的过程中产生优先级反转的情况大体如下:

假设任务1,任务2,任务3;他们的优先级顺序分别为1 > 2 > 3。有一个资源S,S由一个信号量控制为互斥访问。

    任务3正在执行,并申请到了资源S;
    任务1抢占了任务3的执行,任务3挂起,任务1执行;
    任务1申请资源S,发现被占用,所以挂起,任务3恢复执行;
    任务2抢占了任务3的执行,任务3挂起,任务2执行;
    任务2执行完毕,任务3恢复;
    任务3释放资源S,任务1抢占资源S,任务1执行,任务3挂起;
    任务1执行完毕,任务3执行。

以上可以看出,任务2虽然比任务1优先级低,但是比任务1优先执行。也就是说任务1的优先级被降低到了任务3的级别。

解决方法:

    (1)优先级天花板

当任务3使用资源S时,就把任务3的优先级提升到能访问资源S的最高优先级,执行完成释放资源之后,把优先级再改回来;这样的方法,简单易行,解决了多个高优先级任务抢占资源S的问题。但是带来了一些缺点,就是不一定每次都有高优先级任务抢占资源S,每次都提升优先级是对CPU资源的一种浪费。
    (2)优先级继承:

当任务3使用资源S时,任务1抢占执行权,申请资源S,比较资源1和资源3的优先级,假如任务1优先级高,才提升任务3,提升到和任务1相同的优先级,当任务3释放资源后,将优先级再调整回来。相对于优先级天花板方法,相当于延后执行,克服了任务1的缺点,自己本身的特点是,逻辑复杂,需要操作系统支持相同优先级。

2 用pthread_mutex_lock解决优先级反转

linux 应用层提供了pthread_mutex_lock api用于解决由于竞争产生的优先级反转问题。pthread_mutex_lock的逻辑比较简单,就是通过一个原子操作判断锁是否已经被锁住。如果没锁住就更新owner,以及user计数。如果已经被锁住,则陷入内核调用FUTEX_LOCK_PI系统调用完成上锁的操作。而在内核层面,则主要由futex来实现。futex是一种内核的等待机制,等待某个用户态数据被设置成某个值。针对优先级继承的futex略有不同。它不再等待用户传入的地址上的值,而是基于内核rt_mutex机制实现等待,上锁和解锁的功能。下一篇文章讲其具体实现原理,这边只介绍其使用。

2.1 基本api介绍

头文件:#include <pthread.h>

(1)pthread_mutex_init

函数原型: int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_init() 函数是以动态方式创建互斥锁的,参数attr指定了新建互斥锁的属性。如果参数attr为空,则使用默认的互斥锁属性,默认属性为快速互斥锁 。互斥锁的属性在创建锁的时候指定。

pthread_mutexattr_init() 函数成功完成之后会返回零,其他任何返回值都表示出现了错误。

(2)pthread_mutexattr_init

函数原型: int pthread_mutexattr_init(pthread_mutexattr_t *mattr);

pthread_mutexattr_t mattr;

int ret;/* initialize an attribute to default value */

ret = pthread_mutexattr_init(&mattr);

调用此函数时,pshared 属性的缺省值为 PTHREAD_PROCESS_PRIVATE。 该值表示可以在进程内使用经过初始化的互斥锁

mattr 的类型为 opaque,其中包含一个由系统分配的属性对象。mattr 范围可能的值为 PTHREAD_PROCESS_PRIVATE 和 PTHREAD_PROCESS_SHARED。PTHREAD_PROCESS_PRIVATE 是缺省值。

对于互斥锁属性对象,必须首先通过调用 pthread_mutexattr_destroy(3C) 将其销毁,才能重新初始化该对象。pthread_mutexattr_init() 调用会导致分配类型为 opaque 的对象。如果未销毁该对象,则会导致内存泄漏。

pthread_mutexattr_init() 成功完成之后会返回零。其他任何返回值都表示出现了错误。

(3)pthread_mutexattr_destroy

int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr)

pthread_mutexattr_t mattr;

int ret;/* destroy an attribute */

ret = pthread_mutexattr_destroy(&mattr);

pthread_mutexattr_destroy() 成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。

EINVAL 描述: 由 mattr 指定的值无效。

(4)pthread_mutexattr_setpshared

pthread_mutexattr_setpshared(3C) 可用来设置互斥锁变量作用域

int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared);

pthread_mutexattr_t mattr;

int ret;

ret = pthread_mutexattr_init(&mattr); /* * resetting to its default value: private */

ret = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_PRIVATE);

互斥锁变量可以是进程专用的(进程内)变量,也可以是系统范围内的(进程间)变量。要在多个进程中的线程之间共享互斥锁,可以在共享内存中创建互斥锁,并将 pshared 属性设置为 PTHREAD_PROCESS_SHARED。 此行为与最初的 Solaris 线程实现中 mutex_init() 中的 USYNC_PROCESS 标志等效。

如果互斥锁pshared 属性设置为 PTHREAD_PROCESS_PRIVATE,则仅有那些由同一个进程创建的线程才能够处理该互斥锁。

pthread_mutexattr_setpshared() 成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。

EINVAL 描述: 由 mattr 指定的值无效。

(5)pthread_mutexattr_getpshared

pthread_mutexattr_getpshared(3C) 可用来返回由 pthread_mutexattr_setpshared() 定义的互斥锁变量的范围。

int pthread_mutexattr_getpshared(pthread_mutexattr_t *mattr, int *pshared);

pthread_mutexattr_t mattr;

int pshared, ret;/* get pshared of mutex */

ret = pthread_mutexattr_getpshared(&mattr, &pshared);

此函数可为属性对象 mattr 获取 pshared 的当前值。该值为 PTHREAD_PROCESS_SHARED 或 PTHREAD_PROCESS_PRIVATE。

pthread_mutexattr_getpshared() 成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。

EINVAL 描述: 由 mattr 指定的值无效。

(6)pthread_mutexattr_settype

pthread_mutexattr_settype(3C) 可用来设置互斥锁的 type 属性。

int pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type);

类型属性的缺省值为 PTHREAD_MUTEX_DEFAULT。

type 参数指定互斥锁的类型。以下列出了有效的互斥锁类型:

PTHREAD_MUTEX_NORMAL 描述: 此类型的互斥锁不会检测死锁。如果线程在不首先解除互斥锁的情况下尝试重新锁定该互斥锁,则会产生死锁。尝试解除由其他线程锁定的互斥锁会产生不确定的行为。如果尝试解除锁定的互斥锁未锁定,则会产生不确定的行为。 PTHREAD_MUTEX_ERRORCHECK 描述: 此类型的互斥锁可提供错误检查。如果线程在不首先解除锁定互斥锁的情况下尝试重新锁定该互斥锁,则会返回错误。如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。如果线程尝试解除锁定的互斥锁未锁定,则会返回错误。 PTHREAD_MUTEX_RECURSIVE 描述: 如果线程在不首先解除锁定互斥锁的情况下尝试重新锁定该互斥锁,则可成功锁定该互斥锁。 与 PTHREAD_MUTEX_NORMAL 类型的互斥锁不同,对此类型互斥锁进行重新锁定时不会产生死锁情况。多次锁定互斥锁需要进行相同次数的解除锁定才可以释放该锁,然后其他线程才能获取该互斥锁。如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。 如果线程尝试解除锁定的互斥锁未锁定,则会返回错误。 PTHREAD_MUTEX_DEFAULT 描述: 如果尝试以递归方式锁定此类型的互斥锁,则会产生不确定的行为。对于不是由调用线程锁定的此类型互斥锁,如果尝试对它解除锁定,则会产生不确定的行为。对于尚未锁定的此类型互斥锁,如果尝试对它解除锁定,也会产生不确定的行为。允许在实现中将该互斥锁映射到其他互斥锁类型之一。对于 Solaris 线程,PTHREAD_PROCESS_DEFAULT 会映射到 PTHREAD_PROCESS_NORMAL。

如果运行成功,pthread_mutexattr_settype 函数会返回零。否则,将返回用于指明错误的错误号。

EINVAL 描述: 值为 type 无效。EINVAL 描述: attr 指定的值无效。

(7)pthread_mutexattr_gettype

pthread_mutexattr_gettype(3C) 可用来获取由 pthread_mutexattr_settype() 设置的互斥锁的 type 属性。

int pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type);

类型属性的缺省值为 PTHREAD_MUTEX_DEFAULT。

type 参数指定互斥锁的类型。有效的互斥锁类型包括:

PTHREAD_MUTEX_NORMAL PTHREAD_MUTEX_ERRORCHECK PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_DEFAULT 有关每种类型的说明,请参见pthread_mutexattr_settype 语法。

如果成功完成,pthread_mutexattr_gettype() 会返回 0。其他任何返回值都表示出现了错误。

(8)pthread_mutexattr_gettype

pthread_mutexattr_setprotocol(3C) 可用来设置互斥锁属性对象的协议属性。

int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol);

attr 指示以前调用 pthread_mutexattr_init() 时创建的互斥锁属性对象。

protocol 可定义应用于互斥锁属性对象的协议。

pthread.h 中定义的 protocol 可以是以下值之一:PTHREAD_PRIO_NONE、PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT。

PTHREAD_PRIO_NONE 线程的优先级和调度不会受到互斥锁拥有权的影响。PTHREAD_PRIO_INHERIT 此协议值(如 thrd1)会影响线程的优先级和调度。如果更高优先级的线程因 thrd1 所拥有的一个或多个互斥锁而被阻塞,而这些互斥锁是用 PTHREAD_PRIO_INHERIT 初始化的,则 thrd1 将以高于它的优先级或者所有正在等待这些互斥锁(这些互斥锁是 thrd1 指所拥有的互斥锁)的线程的最高优先级运行。如果 thrd1 因另一个线程 (thrd3) 拥有的互斥锁而被阻塞,则相同的优先级继承效应会以递归方式传播给 thrd3。使用 PTHREAD_PRIO_INHERIT 可以避免优先级倒置。低优先级的线程持有较高优先级线程所需的锁时,便会发生优先级倒置。只有在较低优先级的线程释放该锁之后,较高优先级的线程才能继续使用该锁。设置 PTHREAD_PRIO_INHERIT,以便按与预期的优先级相反的优先级处理每个线程。如果为使用协议属性值 PTHREAD_PRIO_INHERIT 初始化的互斥锁定义了 _POSIX_THREAD_PRIO_INHERIT,则互斥锁的属主失败时会执行以下操作。属主失败时的行为取决于 pthread_mutexattr_setrobust_np() 的 robustness 参数的值。解除锁定互斥锁。互斥锁的下一个属主将获取该互斥锁,并返回错误 EOWNERDEAD。互斥锁的下一个属主会尝试使该互斥锁所保护的状态一致。如果上一个属主失败,则状态可能会不一致。如果属主成功使状态保持一致,则可针对该互斥锁调用 pthread_mutex_init() 并解除锁定该互斥锁。注 – 如果针对以前初始化的但尚未销毁的互斥锁调用 pthread_mutex_init(),则该互斥锁不会重新初始化。如果属主无法使状态保持一致,请勿调用 pthread_mutex_init(),而是解除锁定该互斥锁。在这种情况下,所有等待的线程都将被唤醒。以后对 pthread_mutex_lock() 的所有调用将无法获取互斥锁,并将返回错误代码 ENOTRECOVERABLE。现在,通过调用 pthread_mutex_destroy() 来取消初始化该互斥锁,即可使其状态保持一致。调用 pthread_mutex_init() 可重新初始化互斥锁。如果已获取该锁的线程失败并返回 EOWNERDEAD,则下一个属主将获取该锁及错误代码 EOWNERDEAD。PTHREAD_PRIO_PROTECT 当线程拥有一个或多个使用 PTHREAD_PRIO_PROTECT 初始化的互斥锁时,此协议值会影响其他线程(如 thrd2)的优先级和调度。thrd2 以其较高的优先级或者以 thrd2 拥有的所有互斥锁的最高优先级上限运行。基于被 thrd2 拥有的任一互斥锁阻塞的较高优先级线程对于 thrd2 的调度没有任何影响。 如果某个线程调用 sched_setparam() 来更改初始优先级,则调度程序不会采用新优先级将该线程移到调度队列末尾。

线程拥有使用 PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT 初始化的互斥锁线程解除锁定使用 PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT 初始化的互斥锁 一个线程可以同时拥有多个混合使用 PTHREAD_PRIO_INHERIT 和 PTHREAD_PRIO_PROTECT 初始化的互斥锁。在这种情况下,该线程将以通过其中任一协议获取的最高优先级执行。

如果成功完成,pthread_mutexattr_setprotocol() 会返回 0。其他任何返回值都表示出现了错误。

如果出现以下任一情况,pthread_mutexattr_setprotocol() 将失败并返回对应的值。

ENOSYS 描述: 选项 _POSIX_THREAD_PRIO_INHERIT 和 _POSIX_THREAD_PRIO_PROTECT 均未定义并且该实现不支持此函数。 ENOTSUP 描述: protocol 指定的值不受支持。 如果出现以下任一情况,pthread_mutexattr_setprotocol() 可能会失败并返回对应的值。

EINVAL 描述: attr 或 protocol 指定的值无效。EPERM 描述: 调用方无权执行该操作。

(9)pthread_mutexattr_getprotocol

pthread_mutexattr_getprotocol(3C) 可用来获取互斥锁属性对象的协议属性。

int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int *protocol);

attr 指示以前调用 pthread_mutexattr_init() 时创建的互斥锁属性对象。

protocol 包含以下协议属性之一:PTHREAD_PRIO_NONE、PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT。

如果成功完成,pthread_mutexattr_getprotocol() 会返回 0。其他任何返回值都表示出现了错误。

如果出现以下情况,pthread_mutexattr_getprotocol() 将失败并返回对应的值。

ENOSYS 描述: _POSIX_THREAD_PRIO_INHERIT 选项和 _POSIX_THREAD_PRIO_PROTECT 选项均未定义并且该实现不支持此函数。 如果出现以下任一情况,pthread_mutexattr_getprotocol() 可能会失败并返回对应的值。

EINVAL 描述: attr 指定的值无效。EPERM 描述: 调用方无权执行该操作。

(10)pthread_mutexattr_setprioceiling

pthread_mutexattr_setprioceiling(3C) 可用来设置互斥锁属性对象的优先级上限属性。

int pthread_mutexattr_setprioceiling(pthread_mutexatt_t *attr, int prioceiling, int *oldceiling);

attr 指示以前调用 pthread_mutexattr_init() 时创建的互斥锁属性对象。

prioceiling 指定已初始化互斥锁的优先级上限。优先级上限定义执行互斥锁保护的临界段时的最低优先级。prioceiling 位于 SCHED_FIFO 所定义的优先级的最大范围内。要避免优先级倒置,请将 prioceiling 设置为高于或等于可能会锁定特定互斥锁的所有线程的最高优先级。

oldceiling 包含以前的优先级上限值。

如果成功完成,pthread_mutexattr_setprioceiling() 会返回 0。其他任何返回值都表示出现了错误。

如果出现以下任一情况,pthread_mutexattr_setprioceiling() 将失败并返回对应的值。

ENOSYS 描述: 选项 _POSIX_THREAD_PRIO_PROTECT 未定义并且该实现不支持此函数。 如果出现以下任一情况,pthread_mutexattr_setprioceiling() 可能会失败并返回对应的值。

EINVAL 描述: attr 或 prioceiling 指定的值无效。EPERM 描述: 调用方无权执行该操作。

(11)pthread_mutexattr_getprioceiling

pthread_mutexattr_getprioceiling(3C) 可用来获取互斥锁属性对象的优先级上限属性。

int pthread_mutexattr_getprioceiling(const pthread_mutexatt_t *attr, int *prioceiling);

attr 指定以前调用 pthread_mutexattr_init() 时创建的属性对象。

注 :

仅当定义了 _POSIX_THREAD_PRIO_PROTECT 符号时,attr 互斥锁属性对象才会包括优先级上限属性。

pthread_mutexattr_getprioceiling() 返回 prioceiling 中已初始化互斥锁的优先级上限。优先级上限定义执行互斥锁保护的临界段时的最低优先级。prioceiling 位于 SCHED_FIFO 所定义的优先级的最大范围内。要避免优先级倒置,请将 prioceiling 设置为高于或等于可能会锁定特定互斥锁的所有线程的最高优先级。

如果成功完成,pthread_mutexattr_getprioceiling() 会返回 0。其他任何返回值都表示出现了错误。

如果出现以下任一情况,pthread_mutexattr_getprioceiling() 将失败并返回对应的值。

ENOSYS 描述: _POSIX_THREAD_PRIO_PROTECT 选项未定义并且该实现不支持此函数。 如果出现以下任一情况,pthread_mutexattr_getprioceiling() 可能会失败并返回对应的值。

EINVAL 描述: attr 指定的值无效。EPERM 描述: 调用方无权执行该操作。

(12)pthread_mutexattr_setprioceiling

pthread_mutexattr_setprioceiling(3C) 可用来设置互斥锁的优先级上限。

int pthread_mutex_setprioceiling(pthread_mutex_t *mutex, int prioceiling, int *old_ceiling); pthread_mutex_setprioceiling() 可更改互斥锁 mutex 的优先级上限 prioceiling。 pthread_mutex_setprioceiling() 可锁定互斥锁(如果未锁定的话),或者一直处于阻塞状态,直到 pthread_mutex_setprioceiling() 成功锁定该互斥锁,更改该互斥锁的优先级上限并将该互斥锁释放为止。锁定互斥锁的过程无需遵循优先级保护协议。

如果 pthread_mutex_setprioceiling() 成功,则将在 old_ceiling 中返回以前的优先级上限值。如果 pthread_mutex_setprioceiling() 失败,则互斥锁的优先级上限保持不变。

如果成功完成,pthread_mutex_setprioceiling() 会返回 0。其他任何返回值都表示出现了错误。

如果出现以下情况,pthread_mutexatt_setprioceiling() 将失败并返回对应的值。

ENOSYS 描述: 选项_POSIX_THREAD_PRIO_PROTECT 未定义并且该实现不支持此函数。 如果出现以下任一情况,pthread_mutex_setprioceiling() 可能会失败并返回对应的值。

EINVAL 描述: prioceiling 所请求的优先级超出了范围。EINVAL 描述: mutex 指定的值不会引用当前存在的互斥锁。ENOSYS 描述: 该实现不支持互斥锁的优先级上限协议。EPERM 描述: 调用方无权执行该操作。

(13)pthread_mutexattr_getprioceiling

pthread_mutexattr_getprioceiling(3C) 可用来获取互斥锁的优先级上限。

int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex, int *prioceiling); pthread_mutex_getprioceiling() 会返回 mutex 的优先级上限 prioceiling。

如果成功完成,pthread_mutex_getprioceiling() 会返回 0。其他任何返回值都表示出现了错误。

如果出现以下任一情况,pthread_mutexatt_getprioceiling() 将失败并返回对应的值。

ENOSYS 描述: _POSIX_THREAD_PRIO_PROTECT 选项未定义并且该实现不支持此函数。 如果出现以下任一情况,pthread_mutex_getprioceiling() 可能会失败并返回对应的值。

EINVAL 描述: mutex 指定的值不会引用当前存在的互斥锁。ENOSYS 描述: 该实现不支持互斥锁的优先级上限协议。EPERM 描述: 调用方无权执行该操作。

(14)pthread_mutexattr_setrobust_np

pthread_mutexattr_setrobust_np(3C) 可用来设置互斥锁属性对象的强健属性。

int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *attr, int *robustness);

注 – 仅当定义了符号 _POSIX_THREAD_PRIO_INHERIT 时,pthread_mutexattr_setrobust_np() 才适用。

attr 指示以前通过调用 pthread_mutexattr_init() 创建的互斥锁属性对象。

robustness 定义在互斥锁的属主失败时的行为。pthread.h 中定义的 robustness 的值为 PTHREAD_MUTEX_ROBUST_NP 或 PTHREAD_MUTEX_STALLED_NP。缺省值为 PTHREAD_MUTEX_STALLED_NP。

PTHREAD_MUTEX_ROBUST_NP 如果互斥锁的属主失败,则以后对 pthread_mutex_lock() 的所有调用将以不确定的方式被阻塞。 PTHREAD_MUTEX_STALLED_NP 互斥锁的属主失败时,将会解除锁定该互斥锁。互斥锁的下一个属主将获取该互斥锁,并返回错误 EOWNWERDEAD。 注 – 应用程序必须检查 pthread_mutex_lock() 的返回代码,查找返回错误 EOWNWERDEAD 的互斥锁互斥锁的新属主应使该互斥锁所保护的状态保持一致。如果上一个属主失败,则互斥锁状态可能会不一致。 如果新属主能够使状态保持一致,请针对该互斥锁调用 pthread_mutex_consistent_np(),并解除锁定该互斥锁。 如果新属主无法使状态保持一致,请勿针对该互斥锁调用 pthread_mutex_consistent_np(),而是解除锁定该互斥锁。 所有等待的线程都将被唤醒,以后对 pthread_mutex_lock() 的所有调用都将无法获取该互斥锁。返回代码为 ENOTRECOVERABLE。通过调用 pthread_mutex_destroy() 取消对互斥锁的初始化,并调用 pthread_mutex_int() 重新初始化该互斥锁,可使该互斥锁保持一致。如果已获取该锁的线程失败并返回 EOWNERDEAD,则下一个属主获取该锁时将返回代码 EOWNERDEAD。

如果成功完成,pthread_mutexattr_setrobust_np() 会返回 0。其他任何返回值都表示出现了错误。

如果出现以下任一情况,pthread_mutexattr_setrobust_np() 将失败并返回对应的值。

ENOSYS 描述: 选项 _POSIX_THREAD_PRIO__INHERIT 未定义,或者该实现不支持 pthread_mutexattr_setrobust_np()。 ENOTSUP 描述: robustness 指定的值不受支持。 pthread_mutexattr_setrobust_np() 可能会在出现以下情况时失败:

EINVAL 描述: attr 或 robustness 指定的值无效。

(15)pthread_mutexattr_getrobust_np

pthread_mutexattr_getrobust_np(3C) 可用来获取互斥锁属性对象的强健属性。

int pthread_mutexattr_getrobust_np(const pthread_mutexattr_t *attr, int *robustness);

注 – 仅当定义了符号 _POSIX_THREAD_PRIO_INHERIT 时,pthread_mutexattr_getrobust_np() 才适用。

attr 指示以前通过调用 pthread_mutexattr_init() 创建的互斥锁属性对象。

robustness 是互斥锁属性对象的强健属性值。

如果成功完成,pthread_mutexattr_getrobust_np() 会返回 0。其他任何返回值都表示出现了错误。

如果出现以下任一情况,pthread_mutexattr_getrobust_np() 将失败并返回对应的值。

ENOSYS 描述: 选项 _POSIX_THREAD_PRIO__INHERIT 未定义,或者该实现不支持 pthread_mutexattr_getrobust_np()。 pthread_mutexattr_getrobust_np() 可能会在出现以下情况时失败:

EINVAL 描述: attr 或 robustness 指定的值无效。

以上内容引用自该博文:

https://blog.csdn.net/jasmineal/article/details/8807744

2.2 优先级继承example

初始化:

pthread_mutex_t mtx;
pthread_mutexattr_t mtxa;
if(int err = pthread_mutexattr_init(&mtxa))//初始化默认属性
    printf("pthread_mutexattr_init: (%d)%s", err, strerror(err));
//设置mut共享,该属性只有在多进程的时候才有效,多线程不需要设置该属性
if(int err = pthread_mutexattr_setpshared(&mtxa, PTHREAD_PROCESS_SHARED))
    printf("pthread_mutexattr_setpshared: (%d)%s", err, strerror(err));
    // often one process is a real-time and another is not. protect from priority inversion.
//设置优先级继承
if(int err = pthread_mutexattr_setprotocol(&mtxa, PTHREAD_PRIO_INHERIT))
    printf("pthread_mutexattr_setprotocol: (%d)%s", err, strerror(err));
    // if another process is killed while holding the mutex, the other process must be unaffected
//和PTHREAD_PRIO_INHERIT配合使用
if(int err = pthread_mutexattr_setrobust_np(&mtxa, PTHREAD_MUTEX_ROBUST_NP))
    printf("pthread_mutexattr_setrobust: (%d)%s", err, strerror(err));
if(int err = pthread_mutex_init(&mtx, &mtxa))//初始化mtx
    printf("pthread_mutex_init: (%d)%s", err, strerror(err));
pthread_mutexattr_destroy(&mtxa);

经过上面的初始化,在多线程应用时,可以使用pthread_mutex_lock来进行加锁

if(int err = pthread_mutex_lock(&mtx)) 
    printf("pthread_mutex_lock: (%d)%s", err, strerror(err));
    

如果当前锁没有owner,则直接获取该锁,如果当前已经有owner,则会陷入内核,进行优先级继承的相关处理,如果owner的优先级并不高,则会对owner进行提权,使其尽快能获得调度权,而当前锁的竞争者则会暂时进入睡眠,等待锁的释放。

释放锁:

if(int err = pthread_mutex_unlock(&mtx))
     printf("pthread_mutex_unlock: (%d)%s", err, strerror(err));

注:

如果一个互斥锁的持有者没有释放该锁退出了,则在默认情况下当其它线程再去获取这个锁的时候,就会阻塞从而造成死锁。可以更改互斥锁的属性来改变这种默认的方式:
  pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
  pthread_mutexattr_setrobust_np(&mattr,PTHREAD_MUTEX_ROBUST_NP);
通过设置锁的上面两个属性,互斥锁就不再具有默认的行为,当一个锁的owner死掉后,其它线程再去获取这个锁的时候,不会被阻塞,而是会获得这个错,但是同时会得到一个EOWNERDEAD的错误。
然后获得锁的线程可以尝试处理这个错误:

  1. 首先调用pthread_mutex_consistent_np函数来恢复该锁的一致性,
  2. 然后调用pthread_mutex_unlock来解锁
  3. 接下来在调用加锁

这样该锁的行为就恢复正常了。
如果pthread_mutex_consistent_np在恢复锁的一致性时候没有成功,步骤c就不能再执行了,锁也不能被使用了,而且接下来的线程在获取锁都无法获得该锁,而是只能得到返回值ENOTRECOVERABLE。
如果获取某个锁的时候得到了ENOTRECOVERABLE的错误,就意味这这个锁不能被使用了,此时只能调用pthread_mutex_destroy销毁互斥锁然后再调用pthread_mutex_int重新初始化该互斥锁,之后才能再使用该互斥锁。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值