linux线程同步(1)-互斥量

一.概述                                                  

互斥量是线程同步的一种机制,用来保护多线程的共享资源。同一时刻,只允许一个线程对临界区进行访问。

互斥量的工作流程:创建一个互斥量,把这个互斥量的加锁调用放在临界区的开始位置,解锁调用放到临界区的结束位置。当内核优先把某个线程调度到临界区的开始位置时,线程执行这个加锁调用,并进入临界区对资源进行操作。此时其他线程再被内核调度到这里的时候,由于该互斥量已被加锁状态,得不到锁会一直阻塞在这里,导致其他线程不能进入临界区,直到刚刚那个进入临界区的线程离开临界区并执行解锁调用。

二.函数接口                                           

1.初始化互斥量

互斥量是一个pthread_mutex_t类型的变量。

1.1:用宏常量初始化:

 pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

1.2:用函数初始化:

 #include <pthread.h>

 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

mutex:互斥量结构指针

attr:互斥量的属性结构指针

2.设置互斥量属性

 #include <pthread.h>

 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);

attr:互斥量的属性结构指针

type:PTHREAD_MUTEX_NORMAL(默认属性),PTHREAD_MUTEX_ERRORCHECK(会进行错误检查,速度比较慢),PTHREAD_MUTEX_RECURSIVE(递归锁)。对于递归锁,同一个线程对一个递归锁加锁多次,会有一个锁计数器,解锁的时候也需要解锁这个次数才能释放该互斥量。

3.加锁与解锁

 #include <pthread.h>

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

参数都是互斥量指针。pthread_mutex_lock()得不到锁会阻塞,int pthread_mutex_trylock()得不到锁会立即返回,并返回EBUSY错误。

还有一个pthread_mutex_timedlock()会根据时间来等待加锁,如果这段时间得不到锁会返回ETIMEDOUT错误!

 #include <pthread.h>
 #include <time.h>

 int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);

4.销毁互斥量

 #include <pthread.h>

 int pthread_mutex_destroy(pthread_mutex_t *mutex);

mutex:创建的互斥量指针

三.简单例子                                            

写个简单的例子,主线程消费,子线程生产,并模拟使用过程中可能遇到的缺点

 /**
  * @file pthread_mutex.c
  */

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <pthread.h>

 /* 定义互斥量 */
 pthread_mutex_t mtx;
 /* 互斥量属性 */
 pthread_mutexattr_t mtx_attr;
 /* 全局资源 */
 int money;

 void err_exit(const char *err_msg)
 {
     printf("error:%s\n", err_msg);
     exit();
 }

 /* 线程函数 */
 void *thread_fun(void *arg)
 {
     )
     {
         /* 加锁 */
         pthread_mutex_lock(&mtx);

         printf("子线程进入临界区查看money\n");
         )
         {
             money += ;
             printf("子线程:money = %d\n", money);
         }

         /* 解锁 */
         pthread_mutex_unlock(&mtx);

         sleep();
     }

     return NULL;
 }

 int main(void)
 {
     pthread_t tid;

     /* 初始化互斥量属性 */
     )
         err_exit("pthread_mutexattr_init()");

     /* 设置互斥量属性 */
     )
         err_exit("pthread_mutexattr_settype()");

     /* 初始化互斥量 */
     )
         err_exit("pthread_mutex_init()");

     /* 创建一个线程 */
     )
         err_exit("pthread_create()");

     money = ;
     )
     {
         /* 加锁 */
         pthread_mutex_lock(&mtx);

         )
         {
             money -= ;
             printf("主线程:money = %d\n", money);
         }

         /* 解锁 */
         pthread_mutex_unlock(&mtx);

         sleep();
     }

     ;
 }

主线程和子线程都对money的操作进行了互斥量保护。68行,初始化money是1000,主线程每次消耗100,子线程只有到money是0是才会生产。sleep(1)防止独占cpu,也方便打印信息。编译运行:

linux线程同步(1)-互斥量

可以看到这里有个非常浪费资源的问题:主线程消耗money的时候,子线程它不知道什么时候才消耗完,每次内核调度到它时,它都进入临界区加锁互斥量,然后查看money,再解锁。这无意义的操作,简直是极大的浪费!有什么办法可以解决这个问题呢?它有一个好伙伴,叫条件变量

四.死锁                                                  

假设:当线程1获取锁1,再获取锁2后才能进入临界区1,线程2获取锁2,再获取锁1才能进入临界区2。某个时刻,线程1获取了锁1,再去获取锁2的时候发现锁2已经被线程2锁住了,而线程2获取锁2后,发现锁1被线程1锁住了。这样2个线程谁也不让谁,都进不了自己的临界区,就产生了死锁现象!一般遇到这种情况常见的解决办法是:规定统一的加锁顺序。线程1和线程2都按照先锁1,再锁2。还一种就是使用pthread_mutex_trylock(),如果该函数返回EBUSY错误,就释放这个线程的所有锁,不过效率有点低。

上一篇:bzoj千题计划178:bzoj2425: [HAOI2010]计数


下一篇:LeetCode--226--翻转二叉树