linux线程——互斥锁mutex

同步线程中的互斥量(mutex)

本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完以后进行释放互斥量上面的锁。同时互斥变量使用pthread_mutex_t数据类型来表示。mutex对象的值,只有0和1两个值。这两个值代表了mutex的两种状态。0表示锁定状态,当前对象被锁定,用户进程/线程如果试图Lock临界资源,则会进入排队等待;值为1,表示空闲状态,当前对象空闲,用户进程/线程可以Lock临界资源,之后mutex值减1变为0

静态方式:


一、创建与销毁

注意:创建方式两种,销毁方式只有一种。

       

      有两种方式创建锁——动态和静态。

       POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:

       pthread_mutex_t  mutex=PTHREAD_MUTEX_INITIALIZER;

       在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量

动态方式:

      动态方式是使用pthread_mutex_init()函数来初始化互斥锁。

API:

        int pthread_mutex_init(pthread_mutex_t  *restrictmutex, const pthread_mutexattr_t *restrict mutexattr);

mutexattr用于指定互斥锁属性,如果为NULL则使用缺省属性。如果成功返回0,否则返回错误编号。


销毁:

        pthread_mutex_destroy ()用于注销一个互斥锁,API定义如下:

        int pthread_mutex_destroy(pthread_mutex_t *mutex) ;

       销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。


二、锁的类型属性——mutexattr

         互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:

 * PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
* PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
* PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
* PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

、锁操作

       锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

            int pthread_mutex_lock(pthread_mutex_t *mutex);
            int pthread_mutex_unlock(pthread_mutex_t *mutex);
            int pthread_mutex_trylock(pthread_mutex_t *mutex);

         pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。
         对互斥量 进行加锁,需要嗲用pthread_mutex_lock,如果互斥量已经上锁了,调用线程将阻塞知道互斥量被解锁。对互斥量解锁,需要调用pthread_mutex_unlock。
         如果补希望被堵塞,它可以使用pthread_mutex_trylock尝试对互斥量进行加锁,如果调用pthread_mutex_trylock时候互斥两处于未锁住状态那么pthread_mutex_trylock会将锁住互斥两,不会出现阻塞并且返回0,否则pthread)mutex_trylock就会失败,不能锁住互斥量,从而返回EBUSY

四、使用实例—使用mutex互斥量来保护数据结构
#include "apue.h"
#include <pthread.h>
struct foo{
	int f_count;
	pthread_mutex_t f_lock;
};

struct foo* foo_alloc(void)
{
	struct foo *fp;
	if((fp=malloc(sizeof(struct foo)))!=NULL){
		fp->f_count=1;
		if(pthread_mutex_init(&fp->f_lock,NULL)!=0){
			free(fp);
			return(NULL);
		}
	}
	return (fp);
}

void foo_hold(struct foo *fp)
{
	pthread_mutex_lock(&fp->f_lock);
	fp->f_count++;
	pthread_mutex_unlock(&fp->f_lock);
}

void foo_rele(struct foo *fp)
{
	pthread_mutex_lock(&fp->f_lock);
	if(--fp->f_count==0){
		pthread_mutex_unlock(&fp->f_lock);
		pthread_mutex_destroy(&fp->f_lock);
		free(fp);
	} else {
		pthread_mutex_unlock(&fp->f_lock);
	}
}

       该程序描述了用于保护某个数据结构的互斥量是的使用方法。当多个线程要访问动态分配的对象时,可以在对象中嵌入引用计数,确保所有使用该对象的线程完成数据访问之前,该对象内存空间不会被释放。
       引用计数加1、减1以及检查引用计数是否为0这些操作之前,都要锁住互斥。(在初始化的时候没必要加锁,因为在这个操作之前分配线程是唯一引用该对象的线程。
       在使用该对象之前,线程需要先对这个对象的引用计数加1,当对象使用完毕以后,需要对引用计数减1当最后一个引用被释放,对象所占用的内存空间就被释放。

五、避免死锁
       如果线程试图对同一个互斥量加两次锁,那么它自身就会陷入死锁状态,使用互斥量时,还有其他更加不明显的方式也可以产生死锁。
       在避免死锁过程之中,可以先释放占有的锁,然后过一段时间再试。这种情况可以使用pthread_mutex_trylock接口避免死锁。
       如果已经占有某些锁而且pthread_mutex_trylock接口返回成功,那么就可以前进;但是,如果不能获取锁,可以先释放自己已经占有的锁,做好清理工作,然后过一段时间重新尝试。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值