Linux多线程编程三(互斥锁)

        在线程实际运行过程当中,咱们常常须要多个线程保持同步。这时能够用互斥锁来完成任务;互斥锁的使用过程当中,主要有pthread_mutex_init,pthread_mutex_destory,pthread_mutex_lock,pthread_mutex_unlock这几个函数以完成锁的初始化,锁的销毁,上锁和释放锁操做。程序员

一,锁的建立函数

     锁能够被动态或静态建立,能够用宏PTHREAD_MUTEX_INITIALIZER来静态的初始化锁,采用这种方式比较容易理解,互斥锁是pthread_mutex_t的结构体,而这个宏是一个结构常量,以下能够完成静态的初始化锁:测试

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;spa

    另外锁能够用pthread_mutex_init函数动态的建立,函数原型以下:线程

    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t * attr)orm

二,锁的属性对象

    互斥锁属性能够由pthread_mutexattr_init(pthread_mutexattr_t *mattr);来初始化,而后能够调用其余的属性设置方法来设置其属性;递归

    互斥锁的范围:能够指定是该进程与其余进程的同步仍是同一进程内不一样的线程之间的同步。能够设置为队列

PTHREAD_PROCESS_SHARE和PTHREAD_PROCESS_PRIVATE  进程

     默认是后者,表示进程内使用锁。

     可使用int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared)

pthread_mutexattr_getshared(pthread_mutexattr_t *mattr,int *pshared)

用来设置与获取锁的范围;

    互斥锁的类型有如下几个取值空间:      

    PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁之后,其他请求锁的线程将造成一个等待队列,并在解锁后按优先级得到锁。这种锁策略保证了资源分配的公平性。

 PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,容许同一个线程对同一个锁成功得到屡次,并经过屡次unlock解锁。若是是不一样线程请求,则在加锁线程解锁时从新竞争。

 PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,若是同一个线程请求同一个锁,则返回EDEADLK,不然与PTHREAD_MUTEX_TIMED_NP类型动做相同。这样就保证当不容许屡次加锁时不会出现最简单状况下的死锁。

 PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动做最简单的锁类型,仅等待解锁后从新竞争。

能够用

pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type)

pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type)

获取或设置锁的类型。

三,锁的释放

函数原型:

     int pthread_mutex_destroy(pthread_mutex_t *mp); 

     调用pthread_mutex_destory以后,能够释放锁占用的资源,但这有一个前提上锁当前是没有被锁的状态。

返回值:

pthread_mutex_destroy() 在成功完成以后会返回零。其余任何返回值都表示出现了错误。若是出现如下任一状况,该函数将失败并返回对应的值。

EINVAL: mp 指定的值不会引用已初始化的互斥锁对象。

四,锁操做

    对锁的操做主要包括加锁 pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个。

定互斥锁

函数原型:

int pthread_mutex_lock(pthread_mutex_t *mutex); 

函数说明:

当 pthread_mutex_lock() 返回时,该互斥锁已被锁定。调用线程是该互斥锁的属主。若是该互斥锁已被另外一个线程锁定和拥有,则调用线程将阻塞,直到该互斥锁变为可用为止。

返回值:

pthread_mutex_lock() 在成功完成以后会返回零。其余任何返回值都表示出现了错误。若是出现如下任一状况,该函数将失败并返回对应的值。

EAGAIN:因为已超出了互斥锁递归锁定的最大次数,所以没法获取该互斥锁。

EDEADLK:当前线程已经拥有互斥锁。

解除锁定互斥锁

函数原型:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

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

返回值:pthread_mutex_unlock() 在成功完成以后会返回零。

其余任何返回值都表示出现了错误。若是出现如下状况,该函数将失败并返回对应的值。

EPERM :当前线程不拥有互斥锁。

使用非阻塞互斥锁锁定

函数原型:

int pthread_mutex_trylock(pthread_mutex_t *mutex); 

函数说明:pthread_mutex_trylock() 是 pthread_mutex_lock() 的非阻塞版本。若是 mutex 所引用的互斥对象当前被任何线程(包括当前线程)锁定,则将当即返回该调用。不然,该互斥锁将处于锁定状态,调用线程是其属主。

返回值:pthread_mutex_trylock() 在成功完成以后会返回零。其余任何返回值都表示出现了错误。若是出现如下任一状况,该函数将失败并返回对应的值。

EBUSY :

因为 mutex 所指向的互斥锁已锁定,所以没法获取该互斥锁。

EAGAIN:描述:

因为已超出了 mutex 的递归锁定最大次数,所以没法获取该互斥锁。


五,锁的使用

互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见:假设各个线程向同一个文件顺序写入数据,最后获得的结果必定是灾难性的。

咱们先看下面一段代码。这是一个读/写程序,它们公用一个缓冲区,而且咱们假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态:有信息或没有信息。

void reader_function ( void );

void writer_function ( void );

char buffer;

int buffer_has_item=0;

pthread_mutex_t mutex;

struct timespec delay;

void main ( void ){

 pthread_t reader;

 /* 定义延迟时间*/

 delay.tv_sec = 2;

 delay.tv_nec = 0;

 /* 用默认属性初始化一个互斥锁对象*/

 pthread_mutex_init (&mutex,NULL);

pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);

 writer_function( );

}

void writer_function (void){

 while(1){

  /* 锁定互斥锁*/

  pthread_mutex_lock (&mutex);

  if (buffer_has_item==0){

   buffer=make_new_item( );

   buffer_has_item=1;

  }

  /* 打开互斥锁*/

  pthread_mutex_unlock(&mutex);

  pthread_delay_np(&delay);

 }

}

void reader_function(void){

 while(1){

  pthread_mutex_lock(&mutex);

  if(buffer_has_item==1){

   consume_item(buffer);

   buffer_has_item=0;

  }

  pthread_mutex_unlock(&mutex);

  pthread_delay_np(&delay);

 }

}

程序说明:

       这里声明了互斥锁变量mutex,结构pthread_mutex_t为不公开的数据类型,其中包含一个系统分配的属性对象。函数pthread_mutex_init用来生成一个互斥锁。NULL参数代表使用默认属性。若是须要声明特定属性的互斥锁,须调用函数pthread_mutexattr_init。

      pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,若是该锁此时被另外一个线程使用,那此线程被阻塞,即程序将等待到另外一个线程释放此互斥锁。在上面的例子中,咱们使用了pthread_delay_np函数,让线程睡眠一段时间,就是为了防止一个线程始终占据此函数。

六,死锁

       须要提出的是在使用互斥锁的过程当中颇有可能会出现死锁:两个线程试图同时占用两个资源,并按不一样的次序锁定相应的互斥锁,例如两个线程都须要锁定互斥锁1和互斥锁2,a线程先锁定互斥锁1,b线程先锁定互斥锁2,这时就出现了死锁。        有两个方法:

       一、咱们可使用函数pthread_mutex_trylock,它是函数pthread_mutex_lock的非阻塞版本,若是此时互斥量没有被上锁,那么pthread_mutex_trylock将会返回0,并会对该互斥量上锁。若是互斥量已经被上锁,那么会马上返回EBUSY,返回相应的信息,程序员能够针对死锁作出相应的处理。

       二、调用pthread_mutexattr_init()来建立该互斥量,而后调用pthread_mutexattr_settype来设置属性。

一、快速型。这种类型也是默认的类型。 

二、递归型。若是遇到咱们上面所提到的死锁状况,同一线程循环给互斥量上锁,那么系统将会知道该上锁行为来自同一线程,那么就会赞成线程给该互斥量上锁。 

三、错误检测型。若是该互斥量已经被上锁,那么后续的上锁将会失败而不会阻塞,pthread_mutex_lock()操做将会返回EDEADLK。 

七,总结

1.对共享资源操做前必定要得到锁。

2.完成操做之后必定要释放锁。

3.尽可能短期地占用锁。

4.若是有多锁, 如得到顺序是ABC连环扣,释放顺序也应该是ABC。

5.线程错误返回时应该释放它所得到的锁。

相关文章
相关标签/搜索