目录
1.5、pthread_mutexattr_gettype()函数
1.6、pthread_mutexattr_settype()函数
1.7、pthread_mutexattr_init()函数
1.8、pthread_mutexattr_destroy()函数
1.12、pthread_mutex_trylock()函数
2.5、pthread_condattr_destroy()函数
2.9、pthread_cond_timedwait()函数
2.10.1、pthread_condattr_getclock()函数
2.10.2、pthread_condattr_setclock()函数
2.12、pthread_cond_broadcast()函数
1、互斥锁
互斥锁保护的是临界区,实际上保护的是在临界区中被操纵的数据。也就是说,互斥锁通常用于保护由多个线程或进程分享的共享数据。
互斥锁是协作性锁。这就是说,如果共享数据是一个链表,那么操纵该链表的所有线程都必须在实际操纵前获取该互斥锁。不过也没办法防止某个线程不首先获取该互斥量就操纵该链表。
1.1、PTHREAD_MUTEX_INITIALIZER
使用常量PTHREAD_MUTEX_INITIALIZER静态初始化互斥锁。
1.2、pthread_mutex_init()函数
互斥锁一个初始化。
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
参数 mutex:要初始化的互斥锁。
参数 attr:指定互斥锁属性。如果该参数为空指针,那就使用默认属性。
返回值:成功则为0,出错则为正的Exxx值。
1.3、pthread_mutex_destroy()函数
摧毁一个互斥锁。
#include<pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数mutex:要摧毁的互斥锁。
返回值:成功则为0,出错则为正的Exxx值。
1.4、互斥锁类型
PTHREAD_MUTEX_NORMAL | 一种标准的互斥锁类型,不做任何的错误检查或死锁检测。如果线程试图对已经由自己锁定的互斥锁再次进行加锁,则发生死锁;互斥锁处于未锁定状态,或者已由其它线程锁定,对其解锁会导致不确定结果。 |
PTHREAD_MUTEX_ERRORCHECK | 此类互斥锁会提供错误检查(这类互斥锁运行起来比较慢,因为它需要做错误检查,不过可将其作为调试工具,以发现程序哪里违反了互斥锁使用的基本原则)。 譬如这三种情况都会导致返回错误: 1)线程试图对已经由自己锁定的互斥锁再次进行加锁(同一线程对同一互斥锁加锁两次) 2)线程对由其它线程锁定的互斥锁进行解锁; 3)线程对处于未锁定状态的互斥锁进行解锁。 |
PTHREAD_MUTEX_RECURSIVE | 此类互斥锁允许同一线程在互斥锁解锁之前对该互斥锁进行多次加锁,然后维护互斥锁加锁的次数,把这种互斥锁称为递归互斥锁,但是如果解锁次数不等于加锁次数,则是不会释放锁的;所以,如果对一个递归互斥锁加锁两次,然后解锁一次,那么这个 互斥锁依然处于锁定状态,对它再次进行解锁之前不会释放该锁。 |
PTHREAD_MUTEX_DEFAULT | 此 类 互 斥 锁 提 供 默 认 的 行 为 和 特 性(此类锁意在为互斥锁的实现保留最大灵活性 ) 。 使 用 宏 PTHREAD_MUTEX_INITIALIZER 初 始 化 的 互 斥 锁 , 或 者 调 用 参 数 arg 为 NULL 的 pthread_mutexattr_init()函数所创建的互斥锁,都属于此类型。 注:PTHREAD_MUTEX_DEFAULT 类 型 互 斥 锁 的 行 为 与 PTHREAD_MUTEX_NORMAL 类型相仿。 |
1.5、pthread_mutexattr_gettype()函数
获取互斥锁属性类型。
#include <pthread.h>
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type);
参数 attr :指向 pthread_mutexattr_t 类型对象。
参数 type:保存获取的互斥锁类型属性。
返回值:成功返回0,否则返回错误指示错误。
1.6、pthread_mutexattr_settype()函数
设置互斥锁属性类型。
#include <pthread.h>
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
参数 attr :指向 pthread_mutexattr_t 类型对象。
参数 type:设置互斥锁类型属性。
返回值:成功返回0,否则返回错误指示错误。
1.7、pthread_mutexattr_init()函数
初始化一个互斥锁属性。
#include<pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
参数 attr:要初始化的互斥锁的属性。
返回值:成功则为0,出错则为正的Exxx值。
1.8、pthread_mutexattr_destroy()函数
摧毁一个互斥锁属性。
#include<pthread.h>
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
参数 attr:要摧毁的互斥锁的属性。
返回值:成功则为0,出错则为正的Exxx值。
1.9、pthread_mutexattr_getpshared()函数
获取互斥锁共享属性值。
#include<pthread.h>
int pthread_mutexattr_getpshared(const pthread_mutexattr_t
*restrict attr, int *restrict pshared);
参数 attr:属性值。
参数 pshared:获取的共享属性值(返回值为PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED)。
返回值:成功则为0,出错则为正的Exxx值。
1.10、pthread_mutexattr_setpshared()函数
设置互斥锁共享属性值。
#include<pthread.h>
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,
int pshared);
参数 attr:属性值。
参数 pshared:要设置的共享属性值,PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED
返回值:成功则为0,出错则为正的Exxx值
1.11、pthread_mutex_lock()函数
如果尝试给一个已由另外线程锁住的互斥锁上锁,那么将阻塞到该互斥锁解锁为止。
#include<pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
参数 mutex:互斥锁 。
返回值:若成功,返回0,否则,返回错误编号。
1.12、pthread_mutex_trylock()函数
尝试获取互斥锁,如果互斥锁已锁住,那就返回一个EBUSY错误。
#include<pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);
参数 mutex:互斥锁 。
返回值:若成功,返回0,否则,返回错误编号。
1.13、pthread_mutex_unlock()函数
释放一个互斥锁。
#include<pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数 mutex:互斥锁 。
返回值:若成功,返回0,否则,返回错误编号。
2、条件变量
条件变量用于等待。条件变量通常搭配互斥锁来使用, 是因为条件的检测是在互斥锁的保护下进行的, 也就是说条件本身是由互斥锁保护的, 线程在改变条件状态之前必须首先锁住互斥锁, 不然就可能引发线程不安全的问题。
给条件变量发送信号的代码大体如下:
pthread_mutex_lock(mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(mutex);
测试条件并进入睡眠以等待该条件变为真的代码大体如下:
pthread_mutex_lock(mutex);
while(条件为假)
pthread_cond_wait(cond,mutex);
修改条件
pthread_mutex_unlock(mutex);
2.1、PTHREAD_COND_INITIALIZER
使用常量PTHREAD_COND_INITIALIZER静态初始化条件变量。
2.2、pthread_cond_init()函数
初始化条件变量。
#include<pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
参数 cond:要初始化的条件变量
参数 attr:条件变量的属性。
返回值:成功则为0,出错则为正的Exxx值。
2.3、pthread_cond_destroy()函数
摧毁一个条件变量。
#include<pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
参数 cond:要摧毁的条件变量
返回值:成功则为0,出错则为正的Exxx值。
2.4、pthread_condattr_init()函数
初始化条件变量属性值
#include<pthread.h>
int pthread_condattr_init(pthread_condattr_t *attr);
参数 attr:要初始化的条件变量属性
返回值:成功则为0,出错则为正的Exxx值。
2.5、pthread_condattr_destroy()函数
摧毁一个条件变量属性值。
#include<pthread.h>
int pthread_condattr_destroy(pthread_condattr_t *attr);
参数 attr:要摧毁的条件变量属性。
返回值:成功则为0,出错则为正的Exxx值。
2.6、pthread_condattr_getpshared()函数
获取条件变量的共享属性。
#include<pthread.h>
int pthread_condattr_getpshared(const pthread_condattr_t *restrict attr,
int *restrict pshared);
参数 attr:要获取的条件变量共享属性。
参数 pshared:用获取的共享属性值,PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED。
返回值:成功则为0,出错则为正的Exxx值。
2.7、pthread_condattr_setpshared()属性
设置条件变量的共享属性。
#include<pthread.h>
int pthread_condattr_setpshared(pthread_condattr_t *attr,
int pshared);
参数 attr:要设置的条件变量属性。
参数 pshared:设置的共享属性值,PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED。
返回值:成功则为0,出错则为正的Exxx值。
2.8、pthread_cond_wait()函数
该函数原子的执行以下2个动作:
1)给互斥量解锁
2)把调用线程投入睡眠,直到另外某个线程就奔条件变量调用pthread_cond_signal。
注:pthread_cond_wait在返回前重新给互斥锁上锁!!!
注:pthread_cond_wait返回时,总是再次测试相应条件成立与否,因为可能发生虚假的唤醒:期待的条件尚不成立时的唤醒。
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数 cond:要等待的条件变量
参数 mutex:对应的互斥锁
返回值:成功则为0,出错则为正的Exxx值。
2.9、pthread_cond_timedwait()函数
此函数允许线程就阻塞时间设置一个限制值。如果发生超时情况,该函数就返回ETIMEDOUT错误。
#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
参数 cond:条件变量
参数 mutex:对应的互斥锁
参数 abstime:timespec结构,时间值是绝对时间。
返回值:成功则为0,出错则为正的Exxx值。
注:使用绝对时间而不是时间差的好处是,如果函数过早的返回了(也许是因为捕获了某个信号),那么同一个函数无需改变其参数中timespec结构的内容就能再次被调用。
2.10、条件变量时钟
CLOCK_REALTIME | 实时系统时间 |
CLOCK_MONONIC | 不带负跳数的实时系统时间 |
CLOCK_PROCESS_CPUTIME_ID | 调用进程的CPU时间 |
CLOCK_THREAD_CPUTIME_ID | 调用线程的CPU时间 |
2.10.1、pthread_condattr_getclock()函数
获取条件变量的时钟属性。
#include <pthread.h>
int pthread_condattr_getclock(const pthread_condattr_t *attr, clockid_t *id);
2.10.2、pthread_condattr_setclock()函数
设置条件变量的时钟属性。
#include <pthread.h>
int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t id);
2.11、pthread_cond_signal()函数
只唤醒等待在相应条件变量上的一个线程。
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
返回值:成功则为0,出错则为正的Exxx值。
2.12、pthread_cond_broadcast()函数
唤醒阻塞在相应条件变量上的所有线程。
注:考虑条件变量信号单播发送与广播发送的一种候选方式时坚持使用广播发送。如果所有等待者代码都编写确切,只有一个等待者需要唤醒,而且唤醒哪一个等待者无关紧要,那么可以使用为这些情况而优化的单播发送。所有其他情况下都必须使用广播发送。
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
返回值:成功则为0,出错则为正的Exxx值。
3、持有锁期间进程终止
当在进程间共享一个互斥锁时,持有该互斥锁的进程在持有锁期间终止(也许时非自愿的)的可能总是有的。没有办法让系统在进程终止时自动释放所持有的锁(读写锁和Posix信号量也具备这种属性)。进程终止时内核总是自动清理的唯一同步锁类型是fcntl记录锁。使用System V信号量时,应用程序可以选择进程终止时内核是否自动清理某个信号量锁(SEM_UNDO特性)
4、死锁
互斥锁无法解除同时也无法加持,导致程序可能会无限阻塞的情况称为死锁。
1)在互斥锁默认属性的情况下,在同一个线程中不允许对同一互斥锁连续进行加锁操作,因为之前锁处于未解除状态,如果再次对同一个互斥锁进行加锁,那么必然会导致程序无限阻塞等待。
2)多个线程对多个互斥锁交叉使用,每一个线程都试图对其他线程所持有的互斥锁进行加锁。
// 线程 A
pthread_mutex_lock(mutex1);
pthread_mutex_lock(mutex2);
// 线程 B
pthread_mutex_lock(mutex2);
pthread_mutex_lock(mutex1);
注:一个线程中操作多个互斥锁时,加锁与解锁的顺序一定是相反的,否则也会导致错误。