线程同步之互斥锁

目录

一、概述

产生死锁的情况:

一些注意事项:

二、程序接口

头文件:

链接库:

数据结构:

函数清单:

函数详解:

三、示例程序(仅供参考)

四、参考文献


一、概述

互斥锁一般用于线程同步,其主要用于保护临界区,防止多个线程同时修改某些数据。互斥锁使用不当会造成死锁的情况,可能会导致程序卡死,这个需要开发者格外小心。

 

产生死锁的情况:

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 <pthread.h>

#include <errno.h>

 

链接库:

-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 <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>

//定义自己的打印函数
#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网络编程卷二:进程间通信》(中文第二版)

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值