用同步对象编程(多线程编程笔记)

本文详细介绍了互斥锁在多线程编程中的使用,包括初始化、销毁、设置属性和范围,以及处理锁的同步和通信问题。文中还通过实例展示了如何避免死锁和保持数据一致性,强调了同步对象在确保数据安全性中的关键作用。
摘要由CSDN通过智能技术生成

同步对象是内存中的变量,可以按照与访问数据完全相同的方式对其进行访问。不同
进程中的线程可以通过放在由线程控制的共享内存中的同步对象互相通信。尽管不同
进程中的线程通常互不可见,但这些线程仍可以互相通信。
同步对象还可以放在文件中。同步对象可以比创建它的进程具有更长的生命周期。
同步对象具有以下可用类型:
■ 互斥锁
■ 条件变量
■ 读写锁
■ 信号
同步的作用包括以下方面:
■ 同步是确保共享数据一致性的唯一方法。
■ 两个或多个进程中的线程可以合用一个同步对象。由于重新初始化同步对象会将对
象的状态设置为解除锁定,因此应仅由其中的一个协作进程来初始化同步对象。
■ 同步可确保可变数据的安全性。
■ 进程可以映射文件并指示该进程中的线程获取记录锁。一旦获取了记录锁,映射此
文件的任何进程中尝试获取该锁的任何线程都会被阻塞,直到释放该锁为止。
■ 访问一个基本类型变量(如整数)时,可以针对一个内存负荷使用多个存储周期。
如果整数没有与总线数据宽度对齐或者大于数据宽度,则会使用多个存储周期。尽
管这种整数不对齐现象不会出现在SPARC® Platform Edition 体系结构上,但是可移
植的程序却可能会出现对齐问题。

注– 在32 位体系结构上,long long 不是原子类型。(原子操作不能划分成更小的操
作。)long long 可作为两个32 位值进行读写。类型int、char、float 和指针在SPARC
Platform Edition 计算机和Intel 体系结构的计算机上是原子类型。

初始化互斥锁属性对象

使用pthread_mutexattr_init(3C) 可以将与互斥锁对象相关联的属性初始化为其缺省
值。在执行过程中,线程系统会为每个属性对象分配存储空间。
pthread_mutexattr_init语法

int pthread_mutexattr_init(pthread_mutexattr_t *mattr);
#include <pthread.h>
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_PRIVATEPTHREAD_PROCESS_SHAREDPTHREAD_PROCESS_PRIVATE 是缺
省值。
对于互斥锁属性对象,必须首先通过调用pthread_mutexattr_destroy(3C)将其销毁,
才能重新初始化该对象。pthread_mutexattr_init()调用会导致分配类型为opaque 的
对象。如果未销毁该对象,则会导致内存泄漏。

pthread_mutexattr_init 返回值
pthread_mutexattr_init() 成功完成之后会返回零。其他任何返回值都表示出现了错
误。如果出现以下情况,该函数将失败并返回对应的值。
ENOMEM
描述: 内存不足,无法初始化互斥锁属性对象。

销毁互斥锁属性对象

pthread_mutexattr_destroy(3C) 可用来取消分配用于维护pthread_mutexattr_init()
所创建的属性对象的存储空间。
pthread_mutexattr_destroy 语法

int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr)
#include <pthread.h>
pthread_mutexattr_t mattr;
int ret;

/* destroy an attribute */
ret = pthread_mutexattr_destroy(&mattr); 

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

设置互斥锁的范围

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

int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr,
                        int pshared);
#include <pthread.h>
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返回值
pthread_mutexattr_setpshared() 成功完成之后会返回零。其他任何返回值都表示出现
了错误。如果出现以下情况,该函数将失败并返回对应的值。
EINVAL
描述: 由mattr 指定的值无效。

获取互斥锁的范围

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

int pthread_mutexattr_getpshared(pthread_mutexattr_t *mattr,
                            int *pshared);
#include <pthread.h>
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返回值
pthread_mutexattr_getpshared()成功完成之后会返回零。其他任何返回值都表示出现
了错误。如果出现以下情况,该函数将失败并返回对应的值。
EINVAL
描述: 由mattr 指定的值无效。

设置互斥锁类型的属性

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

#include <pthread.h>
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 返回值
如果运行成功,pthread_mutexattr_settype 函数会返回零。否则,将返回用于指明错
误的错误号。
EINVAL
描述: 值为type 无效。
EINVAL
描述: attr 指定的值无效。

获取互斥锁的类型属性

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

#include <pthread.h>
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_gettype 返回值
如果成功完成,pthread_mutexattr_gettype()会返回0。其他任何返回值都表示出现
了错误。

设置互斥锁属性的协议

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

#include <pthread.h>
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol); 

attr 指示以前调用pthread_mutexattr_init() 时创建的互斥锁属性对象。
protocol 可定义应用于互斥锁属性对象的协议。
pthread.h 中定义的protocol 可以是以下值之一:PTHREAD_PRIO_NONE
PTHREAD_PRIO_INHERITPTHREAD_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_INHERITPTHREAD_PRIO_PROTECT初始化的互斥锁
■ 线程解除锁定使用PTHREAD_PRIO_INHERITPTHREAD_PRIO_PROTECT初始化的互斥锁
一个线程可以同时拥有多个混合使用PTHREAD_PRIO_INHERITPTHREAD_PRIO_PROTECT
初始化的互斥锁。在这种情况下,该线程将以通过其中任一协议获取的最高优先级执
行。

pthread_mutexattr_setprotocol 返回值
如果成功完成,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
描述: 调用方无权执行该操作。

获取互斥锁属性的协议

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

#include <pthread.h>
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr,
                        int *protocol); 

attr 指示以前调用pthread_mutexattr_init()时创建的互斥锁属性对象。
protocol 包含以下协议属性之一:PTHREAD_PRIO_NONEPTHREAD_PRIO_INHERIT
PTHREAD_PRIO_PROTECT

pthread_mutexattr_getprotocol返回值
如果成功完成,pthread_mutexattr_getprotocol()会返回0。其他任何返回值都表示出
现了错误。
如果出现以下情况,pthread_mutexattr_getprotocol()将失败并返回对应的值。
ENOSYS
描述: _POSIX_THREAD_PRIO_INHERIT选项和_POSIX_THREAD_PRIO_PROTECT 选项均未定
义并且该实现不支持此函数。
如果出现以下任一情况,pthread_mutexattr_getprotocol()可能会失败并返回对应的
值。
EINVAL
描述: attr 指定的值无效。
EPERM
描述: 调用方无权执行该操作。

设置互斥锁属性的优先级上限

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

#include <pthread.h>
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返回值
如果成功完成,pthread_mutexattr_setprioceiling()会返回0。其他任何返回值都表
示出现了错误。
如果出现以下任一情况,pthread_mutexattr_setprioceiling()将失败并返回对应的
值。
ENOSYS
描述: 选项_POSIX_THREAD_PRIO_PROTECT 未定义并且该实现不支持此函数。
如果出现以下任一情况,pthread_mutexattr_setprioceiling()可能会失败并返回对应
的值。
EINVAL
描述: attr 或prioceiling 指定的值无效。
EPERM
描述: 调用方无权执行该操作。

获取互斥锁属性的优先级上限

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

#include <pthread.h>
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 返回值
如果成功完成,pthread_mutexattr_getprioceiling() 会返回0。其他任何返回值都表
示出现了错误。
如果出现以下任一情况,pthread_mutexattr_getprioceiling() 将失败并返回对应的
值。
ENOSYS
描述: _POSIX_THREAD_PRIO_PROTECT 选项未定义并且该实现不支持此函数。
如果出现以下任一情况,pthread_mutexattr_getprioceiling()可能会失败并返回对应
的值。
EINVAL
描述: attr 指定的值无效。
EPERM
描述: 调用方无权执行该操作。

设置互斥锁的优先级上限

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

#include <pthread.h>
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 返回值
如果成功完成,pthread_mutex_setprioceiling()会返回0。其他任何返回值都表示出
现了错误。
如果出现以下情况,pthread_mutexatt_setprioceiling()将失败并返回对应的值。
ENOSYS
描述: 选项_POSIX_THREAD_PRIO_PROTECT未定义并且该实现不支持此函数。
如果出现以下任一情况,pthread_mutex_setprioceiling()可能会失败并返回对应的
值。
EINVAL
描述: prioceiling 所请求的优先级超出了范围。
EINVAL
描述: mutex 指定的值不会引用当前存在的互斥锁。
ENOSYS
描述: 该实现不支持互斥锁的优先级上限协议。
EPERM
描述: 调用方无权执行该操作。

获取互斥锁的优先级上限

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

#include <pthread.h>
int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex,
            int *prioceiling);

pthread_mutex_getprioceiling() 会返回mutex 的优先级上限prioceiling。

pthread_mutex_getprioceiling返回值
如果成功完成,pthread_mutex_getprioceiling()会返回0。其他任何返回值都表示出
现了错误。
如果出现以下任一情况,pthread_mutexatt_getprioceiling()将失败并返回对应的
值。
ENOSYS
描述:_POSIX_THREAD_PRIO_PROTECT选项未定义并且该实现不支持此函数。
如果出现以下任一情况,pthread_mutex_getprioceiling() 可能会失败并返回对应的
值。
EINVAL
描述: mutex 指定的值不会引用当前存在的互斥锁。
ENOSYS
描述: 该实现不支持互斥锁的优先级上限协议。
EPERM
描述: 调用方无权执行该操作。

设置互斥锁的强健属性

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

#include <pthread.h>
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_NPPTHREAD_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返回值
如果成功完成,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 指定的值无效。

获取互斥锁的强健属性

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

pthread_mutexattr_getrobust_np语法

#include <pthread.h>
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 返回值
如果成功完成,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 指定的值无效。

初始化互斥锁

使用pthread_mutex_init(3C) 可以使用缺省值初始化由mp 所指向的互斥锁,还可以指
定已经使用pthread_mutexattr_init() 设置的互斥锁属性。mattr 的缺省值为NULL。

pthread_mutex_init语法

int pthread_mutex_init(pthread_mutex_t *mp,
                    const pthread_mutexattr_t *mattr);
#include <pthread.h>
pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER;
pthread_mutexattr_t mattr;
int ret;
/* initialize a mutex to its default value */
ret = pthread_mutex_init(&mp, NULL);
/* initialize a mutex */
ret = pthread_mutex_init(&mp, &mattr); 

如果互斥锁已初始化,则它会处于未锁定状态。互斥锁可以位于进程之间共享的内存
中或者某个进程的专用内存中。
注– 初始化互斥锁之前,必须将其所在的内存清零。
将mattr 设置为NULL 的效果与传递缺省互斥锁属性对象的地址相同,但是没有内存开
销。
使用PTHREAD_MUTEX_INITIALIZER 宏可以将以静态方式定义的互斥锁初始化为其缺省属
性。
当其他线程正在使用某个互斥锁时,请勿重新初始化或销毁该互斥锁。如果任一操作
没有正确完成,将会导致程序失败。如果要重新初始化或销毁某个互斥锁,则应用程
序必须确保当前未使用该互斥锁。

pthread_mutex_ini 返回值
pthread_mutex_init() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。
如果出现以下任一情况,该函数将失败并返回对应的值。
EBUSY
描述: 该实现已检测到系统尝试重新初始化mp 所引用的对象,即以前进行过初始化
但尚未销毁的互斥锁。
EINVAL
描述: mattr 属性值无效。互斥锁尚未修改。
EFAULT
描述: mp 所指向的互斥锁的地址无效。

使互斥保持一致

如果某个互斥锁的属主失败,该互斥锁可能会变为不一致。
使用pthread_mutex_consistent_np可使互斥对象mutex 在其属主停止之后保持一致。
pthread_mutex_consistent_np 语法

#include <pthread.h>
int pthread_mutex_consistent_np(pthread_mutex_t *mutex);

注– 仅当定义了_POSIX_THREAD_PRIO_INHERIT 符号时,pthread_mutex_consistent_np()
才适用,并且仅适用于使用协议属性值PTHREAD_PRIO_INHERIT 初始化的互斥锁。
调用pthread_mutex_lock() 会获取不一致的互斥锁。EOWNWERDEAD 返回值表示出现不一
致的互斥锁。
持有以前通过调用pthread_mutex_lock()获取的互斥锁时可调用
pthread_mutex_consistent_np()
如果互斥锁的属主失败,则该互斥锁保护的临界段可能会处于不一致状态。在这种情
况下,仅当互斥锁保护的临界段可保持一致时,才能使该互斥锁保持一致。
针对互斥锁调用pthread_mutex_lock()pthread_mutex_unlock()
pthread_mutex_trylock()会以正常方式进行。
对于不一致或者未持有的互斥锁,pthread_mutex_consistent_np()的行为是不确定
的。

pthread_mutex_consistent_np 返回值
pthread_mutex_consistent_np()在成功完成之后会返回零。其他任何返回值都表示出
现了错误。
pthread_mutex_consistent_np() 会在出现以下情况时失败:
ENOSYS
描述: 选项_POSIX_THREAD_PRIO_INHERIT 未定义,或者该实现不支持
pthread_mutex_consistent_np()
pthread_mutex_consistent_np()可能会在出现以下情况时失败:
EINVAL
描述: mattr 属性值无效。

锁定互斥锁

使用pthread_mutex_lock(3C) 可以锁定mutex 所指向的互斥锁。
pthread_mutex_lock 语法

int pthread_mutex_lock(pthread_mutex_t *mutex);
#include <pthread.h>
pthread_mutex_t mutex;
int ret; 

ret = pthread_ mutex_lock(&mp); /* acquire the mutex */ 

pthread_mutex_lock() 返回时,该互斥锁已被锁定。调用线程是该互斥锁的属主。
如果该互斥锁已被另一个线程锁定和拥有,则调用线程将阻塞,直到该互斥锁变为可
用为止。
如果互斥锁类型为PTHREAD_MUTEX_NORMAL,则不提供死锁检测。尝试重新锁定互斥锁会
导致死锁。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或未锁定,则将产
生不确定的行为。
如果互斥锁类型为PTHREAD_MUTEX_ERRORCHECK,则会提供错误检查。如果某个线程尝试
重新锁定的互斥锁已经由该线程锁定,则将返回错误。如果某个线程尝试解除锁定的
互斥锁不是由该线程锁定或者未锁定,则将返回错误。
如果互斥锁类型为PTHREAD_MUTEX_RECURSIVE,则该互斥锁会保留锁定计数这一概念。
线程首次成功获取互斥锁时,锁定计数会设置为1。线程每重新锁定该互斥锁一次,锁
定计数就增加1。线程每解除锁定该互斥锁一次,锁定计数就减小1。锁定计数达到0
时,该互斥锁即可供其他线程获取。如果某个线程尝试解除锁定的互斥锁不是由该线
程锁定或者未锁定,则将返回错误。
如果互斥锁类型是PTHREAD_MUTEX_DEFAULT,则尝试以递归方式锁定该互斥锁将产生不
确定的行为。对于不是由调用线程锁定的互斥锁,如果尝试解除对它的锁定,则会产
生不确定的行为。如果尝试解除锁定尚未锁定的互斥锁,则会产生不确定的行为。

pthread_mutex_lock返回值
pthread_mutex_lock()在成功完成之后会返回零。其他任何返回值都表示出现了错误。
如果出现以下任一情况,该函数将失败并返回对应的值。
EAGAIN
描述: 由于已超出了互斥锁递归锁定的最大次数,因此无法获取该互斥锁。
EDEADLK
描述: 当前线程已经拥有互斥锁。
如果定义了_POSIX_THREAD_PRIO_INHERIT 符号,则会使用协议属性值
PTHREAD_PRIO_INHERIT对互斥锁进行初始化。此外,如果
pthread_mutexattr_setrobust_np()的robustness 参数是PTHREAD_MUTEX_ROBUST_NP,则
该函数将失败并返回以下值之一:

EOWNERDEAD
描述: 该互斥锁的最后一个属主在持有该互斥锁时失败。该互斥锁现在由调用方拥
有。调用方必须尝试使该互斥锁所保护的状态一致。
如果调用方能够使状态保持一致,请针对该互斥锁调用
pthread_mutex_consistent_np()并解除锁定该互斥锁。以后对
pthread_mutex_lock( 的调用都将正常进行。
如果调用方无法使状态保持一致,请勿针对该互斥锁调用pthread_mutex_init()
但要解除锁定该互斥锁。以后调用pthread_mutex_lock() 时将无法获取该互斥锁,
并且将返回错误代码ENOTRECOVERABLE
如果获取该锁的属主失败并返回EOWNERDEAD,则下一个属主获取该锁时将返回
EOWNERDEAD

ENOTRECOVERABLE
描述: 尝试获取的互斥锁正在保护某个状态,此状态由于该互斥锁以前的属主在持有
该锁时失败而导致不可恢复。尚未获取该互斥锁。如果满足以下条件,则可能出现
此不可恢复的情况:
■ 以前获取该锁时返回EOWNERDEAD
■ 该属主无法清除此状态
■ 该属主已经解除锁定了该互斥锁,但是没有使互斥锁状态保持一致

ENOMEM
描述: 已经超出了可同时持有的互斥锁数目的限制。

解除锁定互斥锁

使用pthread_mutex_unlock(3C)可以解除锁定mutex 所指向的互斥锁。
pthread_mutex_unlock语法

int pthread_mutex_unlock(pthread_mutex_t *mutex);
#include <pthread.h>
pthread_mutex_t mutex;
int ret; 

ret = pthread_mutex_unlock(&mutex); /* release the mutex */

pthread_mutex_unlock()可释放mutex 引用的互斥锁对象。互斥锁的释放方式取决于互
斥锁的类型属性。如果调用pthread_mutex_unlock() 时有多个线程被mutex 对象阻
塞,则互斥锁变为可用时调度策略可确定获取该互斥锁的线程。对于
PTHREAD_MUTEX_RECURSIVE类型的互斥锁,当计数达到零并且调用线程不再对该互斥锁
进行任何锁定时,该互斥锁将变为可用。

pthread_mutex_unlock 返回值
pthread_mutex_unlock() 在成功完成之后会返回零。其他任何返回值都表示出现了错
误。如果出现以下情况,该函数将失败并返回对应的值。
EPERM
描述: 当前线程不拥有互斥锁。

使用非阻塞互斥锁锁定

使用pthread_mutex_trylock(3C) 可以尝试锁定mutex 所指向的互斥锁。
pthread_mutex_trylock语法

int pthread_mutex_trylock(pthread_mutex_t *mutex);
#include <pthread.h>
pthread_mutex_t mutex;
int ret; 

ret = pthread_mutex_trylock(&mutex); /* try to lock the mutex */ 

pthread_mutex_trylock()pthread_mutex_lock()的非阻塞版本。如果mutex 所引用
的互斥对象当前被任何线程(包括当前线程)锁定,则将立即返回该调用。否则,该
互斥锁将处于锁定状态,调用线程是其属主。

pthread_mutex_trylock 返回值
pthread_mutex_trylock()在成功完成之后会返回零。其他任何返回值都表示出现了错
误。如果出现以下任一情况,该函数将失败并返回对应的值。
EBUSY
描述: 由于mutex 所指向的互斥锁已锁定,因此无法获取该互斥锁。
EAGAIN
描述: 由于已超出了mutex 的递归锁定最大次数,因此无法获取该互斥锁。
如果定义了_POSIX_THREAD_PRIO_INHERIT 符号,则会使用协议属性值
PTHREAD_PRIO_INHERIT 对互斥锁进行初始化。此外,如果
pthread_mutexattr_setrobust_np()的robustness 参数是PTHREAD_MUTEX_ROBUST_NP,则
该函数将失败并返回以下值之一:

EOWNERDEAD
描述: 该互斥锁的最后一个属主在持有该互斥锁时失败。该互斥锁现在由调用方拥
有。调用方必须尝试使该互斥锁所保护的状态一致。
如果调用方能够使状态保持一致,请针对该互斥锁调用
pthread_mutex_consistent_np()并解除锁定该互斥锁。以后对
pthread_mutex_lock() 的调用都将正常进行。
如果调用方无法使状态保持一致,请勿针对该互斥锁调用pthread_mutex_init()
而要解除锁定该互斥锁。以后调用pthread_mutex_trylock()时将无法获取该互斥
锁,并且将返回错误代码ENOTRECOVERABLE
如果已获取该锁的属主失败并返回EOWNERDEAD,则下一个属主获取该锁时返回
EOWNERDEAD。

ENOTRECOVERABLE
描述: 尝试获取的互斥锁正在保护某个状态,此状态由于该互斥锁以前的属主在持有
该锁时失败而导致不可恢复。尚未获取该互斥锁。以下条件下可能会出现此情况:
■ 以前获取该锁时返回EOWNERDEAD
■ 该属主无法清除此状态
■ 该属主已经解除锁定了该互斥锁,但是没有使互斥锁状态保持一致

ENOMEM
描述: 已经超出了可同时持有的互斥锁数目的限制。

销毁互斥锁

使用pthread_mutex_destroy(3C)可以销毁与mp 所指向的互斥锁相关联的任何状态。

pthread_mutex_destroy语法

int pthread_mutex_destroy(pthread_mutex_t *mp);
#include <pthread.h>
pthread_mutex_t mp;
int ret; 

ret = pthread_mutex_destroy(&mp); /* mutex is destroyed */ 

请注意,没有释放用来存储互斥锁的空间。
pthread_mutex_destroy返回值
pthread_mutex_destroy()在成功完成之后会返回零。其他任何返回值都表示出现了错
误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述: mp 指定的值不会引用已初始化的互斥锁对象。

互斥锁定的代码示例

示例1 显示了使用互斥锁定的一些代码段。
示例1互斥锁示例:

#include <pthread.h>
pthread_mutex_t count_mutex;
long long count;
void increment_count()
{
    pthread_mutex_lock(&count_mutex);
    count = count + 1;
    pthread_mutex_unlock(&count_mutex);
}
long long get_count()
{
    long long c;
    pthread_mutex_lock(&count_mutex);
    c = count;
    pthread_mutex_unlock(&count_mutex);
    return (c);
} 

示例1 中的两个函数将互斥锁用于不同目的。increment_count()函数使用互斥锁确
保对共享变量进行原子更新。get_count() 函数使用互斥锁保证以原子方式读取64 位数
量count。在32 位体系结构上,long long 实际上是两个32 位数量。
读取整数值时执行的是原子运算,因为整数是大多数计算机中常见的字长。

锁分层结构的使用示例

有时,可能需要同时访问两个资源。您可能正在使用其中的一个资源,随后发现还需
要另一个资源。如果两个线程尝试声明这两个资源,但是以不同的顺序锁定与这些资
源相关联的互斥锁,则会出现问题。例如,如果两个线程分别锁定互斥锁1 和互斥锁
2,则每个线程尝试锁定另一个互斥锁时,将会出现死锁。示例4–2 说明了可能的死锁
情况。

示例2死锁

线程1
pthread_mutex_lock(&m1);/* use resource 1 */
pthread_mutex_lock(&m2);/* use resources 1 and 2 */
pthread_mutex_unlock(&m2);
pthread_mutex_unlock(&m1);
线程2
pthread_mutex_lock(&m2);/* use resource 2 */
pthread_mutex_lock(&m1);/* use resources 1 and 2 */
pthread_mutex_unlock(&m1);
pthread_mutex_unlock(&m2);

避免此问题的最佳方法是,确保线程在锁定多个互斥锁时,以同样的顺序进行锁定。
如果始终按照规定的顺序锁定,就不会出现死锁。此方法称为锁分层结构,它通过为
互斥锁指定逻辑编号来对这些锁进行排序。
另外,请注意以下限制:如果您持有的任何互斥锁其指定编号大于n,则不能提取指定
编号为n 的互斥锁。
但是,不能始终使用此方法。有时,必须按照与规定不同的顺序提取互斥锁。要防止
在这种情况下出现死锁,请使用pthread_mutex_trylock()。如果线程发现无法避免死
锁时,该线程必须释放其互斥锁。
示例3条件锁定:

线程1
pthread_mutex_lock(&m1);
pthread_mutex_lock(&m2);
/* no processing */
pthread_mutex_unlock(&m2);
pthread_mutex_unlock(&m1);
线程2
for (; ;)
{ pthread_mutex_lock(&m2);
if(pthread_mutex_trylock(&m1)==0)/* got it */
break;/* didn't get it */
pthread_mutex_unlock(&m2);
}/* get locks; no processing */
pthread_mutex_unlock(&m1);
pthread_mutex_unlock(&m2);

在示例3 中,线程1 按照规定的顺序锁定互斥锁,但是线程2 不按顺序提取互斥锁。
要确保不会出现死锁,线程2 必须非常小心地提取互斥锁1。如果线程2 在等待该互斥
锁释放时被阻塞,则线程2 可能刚才已经与线程1 进入了死锁状态。
要确保线程2 不会进入死锁状态,线程2 需要调用pthread_mutex_trylock(),此函数
可在该互斥锁可用时提取它。如果该互斥锁不可用,线程2 将立即返回并报告提取失
败。此时,线程2 必须释放互斥锁2。线程1 现在会锁定互斥锁2,然后释放互斥锁1
和互斥锁2。

嵌套锁定和单链接列表的结合使用示例

示例4 和示例5 说明了如何同时提取三个锁。通过按照规定的顺序提取锁可避免出
现死锁。
示例4单链接列表结构

typedef struct node1 {
    int value;
    struct node1 *link;
    pthread_mutex_t lock;
} node1_t;
node1_t ListHead; 

本示例针对每个包含一个互斥锁的节点使用单链接列表结构。要将某个节点从列表中
删除,请首先从ListHead 开始搜索列表,直到找到所需的节点为止。ListHead 永远不会
被删除。
要防止执行此搜索时产生并发删除,请在访问每个节点的任何内容之前先锁定该节
点。由于所有的搜索都从ListHead 开始,并且始终按照列表中的顺序提取锁,因此不
会出现死锁。
因为更改涉及到两个节点,所以找到所需的节点之后,请锁定该节点及其前序节点。
因为前序节点的锁总是最先提取,所以可再次防止出现死锁。示例5 说明如何使用C
代码来删除单链接列表中的项。
示例5单链接列表和嵌套锁定

node1_t *delete(int value)
{
    node1_t *prev, *current;
    prev = &ListHead;
    pthread_mutex_lock(&prev->lock);
    while ((current = prev->link) != NULL) {
        pthread_mutex_lock(&current->lock);
        if (current->value == value) {
            prev->link = current->link;
            pthread_mutex_unlock(&current->lock);
            pthread_mutex_unlock(&prev->lock);
            current->link = NULL;
            return(current);
        }
        pthread_mutex_unlock(&prev->lock);
        prev = current;
    }
    pthread_mutex_unlock(&prev->lock);
    return(NULL);
} 

嵌套锁定和循环链接列表的示例

示例6 通过将以前的列表结构转换为循环列表来对其进行修改。由于不再存在用于
标识的头节点,因该线程可以与特定的节点相关联,并可针对该节点及其邻居执行操
作。锁分层结构在此处不适用,因为链接之后的分层结构明显是循环结构。

示例6循环链接列表结构

typedef struct node2 {
    int value;
    struct node2 *link;
    pthread_mutex_t lock;
} node2_t; 

以下的C 代码用来获取两个节点上的锁并执行涉及到这两个锁的操作。
示例7循环链接列表和嵌套锁定

void Hit Neighbor(node2_t *me) {
while (1) {
    pthread_mutex_lock(&me->lock);
    if (pthread_mutex_lock(&me->link->lock)!= 0) {
            /* failed to get lock */
            pthread_mutex_unlock(&me->lock);
            continue;
        }
        break;
    }
    me->link->value += me->value;
    me->value /=2;
    pthread_mutex_unlock(&me->link->lock);
    pthread_mutex_unlock(&me->lock);
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值