目录
一、概述
产生死锁的情况:
一些注意事项:
二、程序接口
头文件:
链接库:
数据结构:
函数清单:
函数详解:
三、示例程序(仅供参考)
四、参考文献
互斥锁一般用于线程同步,其主要用于保护临界区,防止多个线程同时修改某些数据。互斥锁使用不当会造成死锁的情况,可能会导致程序卡死,这个需要开发者格外小心。
1、一个函数里面对一个互斥锁加锁之后,因为某个原因,没有解锁就结束了。比如:函数中没有解锁就return了。(对于这种情况我一般是使用goto替代return进行处理,就是在函数末尾设置一个标签,跳转至该标签会进行解锁操作)
2、多个线程多个互斥锁的情况,线程1对互斥锁A加锁,然后它试图访问线程2的资源,对互斥锁B尝试上锁,但是线程2已经对互斥锁B加锁了,同时线程2也尝试访问线程1的资源,对互斥锁A尝试上锁,这样的话两个线程就会出现互相等待对方释放互斥锁的情况。(对于这种情况,我一般是把这次操作所有需要获取的锁在开头就获取好,而不是在需要的时候才来获取)
3、当持有某个锁的线程异常退出的时候,这个时候线程来不及释放锁,会造成死锁的情况。(对于该情况可以通过设置互斥锁的“健壮性”属性来避免。)
1、互斥锁使用之前一定要进行初始化,否则会出现错误并导致程序崩溃,初始化可以使用宏PTHREAD_MUTEX_INITIALIZER(全局互斥锁)或pthread_mutex_init()函数。
2、互斥锁属性结构使用之前也一定要初始化,否则会造成互斥锁无法上锁的情况。
3、某个线程对互斥锁进行上锁操作,那么解锁操作一定要在本线程来进行,不能由其他线程进行解锁(会返回错误)。
#include
#include
-lpthread
pthread_mutext_t:互斥锁结构
pthread_mutexattr_t:互斥锁属性结构
注意:以上两个结构都需要初始化才能用,否则会出现不可预知的错误
int pthread_mutex_init(pthread_mutext_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_unlock(pthread_mutex *mutex);
int pthread_mutexattr_init(pthread_mutexattr_t* attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t* attr);
int pthread_mutexattr_getrobust(const pthread_mutexattr_t *attr, int *robustness);
int pthread_mutexattr_setrobust(const pthread_mutexattr_t *attr, int robustness);
int pthread_mutex_consistent(pthread_mutex_t *mutex);
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type);
int pthread_mutexattr_gettype(const pthread_mutexattr_t* restrict attr, int* type);
int pthread_mutex_init(pthread_mutext_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
该函数用于初始化互斥锁,参数如下:
mutex:需要进行初始化的互斥锁。
attr:互斥锁的属性,如果为NULL则使用默认属性。
返回值:0为成功;否则返回错误编号
int pthread_mutex_destroy(pthread_mutex_t *mutex);
该函数用于销毁互斥锁,参数如下:
mutex:需要进行销毁的互斥锁。
返回值:0为成功;否则返回错误编号。
int pthread_mutex_lock(pthread_mutex *mutex);
该函数用于互斥锁上锁,参数如下:
mutex:互斥锁结构指针。
返回值:0为成功;否则返回错误编号。
int pthread_mutex_unlock(pthread_mutex *mutex);
该函数用于互斥锁解锁,参数如下:
mutex:互斥锁结构指针。
返回值:0为成功;否则返回错误编码。
int pthread_mutexattr_init(pthread_mutexattr_t* attr);
该函数用于初始化互斥锁属性结构,参数如下:
attr:互斥锁属性结构
返回值:0为成功;否则返回错误编号
int pthread_mutexattr_destroy(pthread_mutexattr_t* attr);
该函数用于销毁互斥锁属性结构,参数如下:
attr:互斥锁属性结构
返回值:0为成功;否则返回错误编号
int pthread_mutexattr_getrobust(const pthread_mutexattr_t *attr, int *robustness);
该函数用于获取互斥锁的健壮属性,参数如下:
attr:互斥锁属性结构指针。
robustness:用于获取健壮属性的变量。
返回值:0为成功,否则返回错误编号。
int pthread_mutexattr_setrobust(const pthread_mutexattr_t *attr, int robustness);
该函数用于设置互斥锁的“健壮性”,以防止因为某个线程/进程的异常退出,造成其持有的互斥锁未解锁,进而导致其他线程/进程死锁的情况。设置了该属性之后,当某个线程/进程异常退出时,该互斥锁会使阻塞在 pthread_mutex_lock()的线程返回EOWNERDEAD(有多个线程在等待互斥锁的时候,只有一个会返回EOWNERDEAD,其他返回非0的错误值),之后若需要继续使用该互斥量,应该先调用pthread_mutex_consistent,然后再对其进行解锁,或将其重新初始化(先销毁再初始化),即恢复了之后才能继续使用,否则直接加锁仍会造成死锁的情况(PS:返回EOWNERDEAD之后,如果直接对该互斥锁先进行解锁操作,然后再加锁,那么这次的加锁操作会失败,也就是这个互斥锁已经是不可用的状态了)。参数如下:
attr:互斥锁属性结构指针。
robustness:设置的健壮性的值,默认为PTHREAD_MUTEX_STALLED,设置为该值时,线程/进程异常退出时,会导致死锁的情况;当设置为PTHREAD_MUTEX_ROBUST时,则线程/进程异常退出时pthread_mutex_lock()会返回EOWNERDEAD。
返回值:0为成功,否则返回错误编号。
加了该属性之后,对于获取锁失败的情况,可以参考如下代码处理:
ret = pthread_mutex_lock(&myMutex);
if(ret != 0)
{
if(ret == EOWNERDEAD)
{
pthread_mutex_consistent(&myMutex);
pthread_mutex_unlock(&myMutex);
pthread_mutex_lock(&myMutex);
}
}
int pthread_mutex_consistent(pthread_mutex_t *mutex);
该函数用于“健壮”的互斥锁,当pthread_mutex_lock返回EOWNERDEAD时,应该先使用该函数设置互斥锁,然后再解锁,否则会出现后面无法再对该互斥锁进行加锁操作的情况。参数如下:
mutex:互斥锁指针
返回值:0为成功,否则返回错误编号。
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared);
该函数用于获取互斥锁的“共享”属性,参数如下:
attr: 互斥锁属性结构指针
pshared:需要获取的“共享”属性
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
该函数用于设置互斥锁的“共享”属性,设置了“共享”属性之后,互斥锁可放在共享内存用于进程同步,参数如下:
attr: 互斥锁属性结构指针
pshared:需要设置的“共享”属性
返回值:0为成功,否则返回错误编号
int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type);
该函数用于设置互斥锁的类型属性,有以下几种类型:
参数如下:
attr:互斥锁属性结构指针
type:类型
返回值:0为成功,否则返回错误编号
int pthread_mutexattr_gettype(const pthread_mutexattr_t* restrict attr, int* type);
用于获取互斥锁的类型属性,参数如下:
attr:互斥锁属性结构指针
type:类型指针,类型可以参考上面的图
返回值:0为成功,否则返回错误编号
#include "output.h"
#include
#include
#include
#include
//定义自己的打印函数
#define Output_Printf(str, args...) printf("[%s:%d]" str, __FUNCTION__, __LINE__, ##args)
pthread_mutex_t myMutex;
//互斥锁测试线程
static void* Thread_MutexTest(void* arg)
{
int threadID = *(int*)arg;
int ret = 0;
Output_Printf("Thread[%d] start!\n", threadID);
ret = pthread_mutex_lock(&myMutex);
if(ret != 0)
{
if(ret == EOWNERDEAD)//对于持有锁的线程突然挂掉之后的处理
{
Output_Printf("Thread[%d]: Mutex lock owner dead!\n", threadID);
pthread_mutex_consistent(&myMutex);
pthread_mutex_unlock(&myMutex);
pthread_mutex_lock(&myMutex);
Output_Printf("Thread[%d] Lock again!\n", threadID);
}
else
Output_Printf("pthread_mutex_lock error!\n");
}
Output_Printf("Thread[%d] lock!\n", threadID);//成功获取到锁
sleep(2);
if(threadID == 1)//制作某个线程在持有锁的情况下异常退出的情景,测试robust属性
return NULL;
pthread_mutex_unlock(&myMutex);
Output_Printf("Thread[%d] unlock!\n", threadID);
return NULL;
}
//互斥锁
void Mutex_Test()
{
pthread_mutexattr_t myMutexAttr;
int ret = 0, threadNum = 3, loop = 0;
pthread_t pthreadID;
//初始化互斥锁的属性结构
ret = pthread_mutexattr_init(&myMutexAttr);
if(ret != 0)
{
Output_Printf("Can not init mutex attribute!\n");
return;
}
//设置robust属性
ret = pthread_mutexattr_setrobust(&myMutexAttr, PTHREAD_MUTEX_ROBUST);
if(ret != 0)
{
Output_Printf("Can not set mutex robust attribute!\n");
return;
}
//用设置好的属性初始化互斥锁
ret = pthread_mutex_init(&myMutex, &myMutexAttr);
if(ret != 0)
{
Output_Printf("Init mutex fail!\n");
return;
}
//创建线程
for(loop = 0; loop < threadNum; loop++)
{
pthread_create(&pthreadID, NULL, Thread_MutexTest, &loop);
sleep(1);
}
//等待上锁
ret = pthread_mutex_lock(&myMutex);
if(ret != 0)
{
if(ret == EOWNERDEAD)//对于持有锁的线程突然挂掉之后的处理
{
Output_Printf("Main: Mutex lock owner dead!\n");
pthread_mutex_consistent(&myMutex);
pthread_mutex_unlock(&myMutex);
pthread_mutex_lock(&myMutex);
Output_Printf("Lock again!\n");
}
else
Output_Printf("Main pthread_mutex_lock error!\n");
}
Output_Printf("Main lock!\n");//成功获取到锁
sleep(2);
pthread_mutex_unlock(&myMutex);
Output_Printf("Main unlock!\n");
//防止主线程释放锁后马上结束,导致整个程序退出
getchar();
}
上面程序的运行结果如下:
逐句分析打印信息:
[Thread_MutexTest:24]Thread[0] start! //线程0启动。
[Thread_MutexTest:38]Thread[0] lock! //线程0获取到互斥锁。
[Thread_MutexTest:24]Thread[1] start! //线程1启动,因为现在是线程0持有互斥锁,所以线程1阻塞。
[Thread_MutexTest:43]Thread[0] unlock! //线程0释放互斥锁。
[Thread_MutexTest:38]Thread[1] lock! //线程1获取到互斥锁,并且根据程序设定,线程1将不释放互斥锁就退出线程,制造线程异常退出的情况。
[Thread_MutexTest:24]Thread[2] start! //线程2启动。
[Thread_MutexTest:30]Thread[2]: Mutex lock owner dead! //由于线程1在持有互斥锁的时候就退出了,因此线程2在获取互斥锁的时候将出现EOWNERDEAD的错误,需进行处理。
[Mutex_Test:100]Main lock! //线程2执行了pthread_mutex_consistent(&myMutex)和pthread_mutex_unlock(&myMutex)之后,主线程main先获取到互斥锁,此时线程2将阻塞。
[Mutex_Test:103]Main unlock! //主线程释放锁。
[Thread_MutexTest:38]Thread[2] lock! //线程2终于获取到锁。
[Thread_MutexTest:43]Thread[2] unlock! //线程释放锁。
//程序结束
1、《Unix环境高级编程》(中文第三版)
2、《Unix网络编程卷二:进程间通信》(中文第二版)