一生有你llx 阅读(87) 评论(0)
一、为什么要使用互斥量
1、当多个线程共享相同的内存时,需要每一个线程看到相同的视图。当一个线程修改变量时,而其他线程也可以读取或者修改这个变量,就需要对
    这些线程同步,确保他们不会访问到无效的变量
2、在变量修改时间多于一个存储器访问周期的处理器结构中,当存储器的读和写这两个周期交叉时,这种潜在的不一致性就会出现。当然这与处理
    器相关,但是在可移植的程序中并不能对处理器做出任何假设

二、互斥量的初始化
1、为了让线程访问数据不产生冲突,这要就需要对变量加锁,使得同一时刻只有一个线程可以访问变量。互斥量本质就是锁,访问共享资源前对
    互斥量加锁,访问完成后解锁
2、当互斥量加锁以后,其他所有需要访问该互斥量的线程都将阻塞
3、当互斥量解锁以后,所有因为这个互斥量阻塞的线程都将变为就绪态,第一个获得cpu的线程会获得互斥量,变为运行态,而其他线程会继续
    变为阻塞,在这种方式下访问互斥量每次只有一个线程能向前执行
4、互斥量用pthread_mutex_t类型的数据表示,在使用之前需要对互斥量初始化
    1)、如果是动态分配的互斥量,可以调用pthread_mutex_init()函数初始化
    2)、如果是静态分配的互斥量,还可以把它置为常量PTHREAD_MUTEX_INITIALIZER
    3)、动态分配的互斥量在释放内存之前需要调用pthread_mutex_destroy()

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

三、加锁和解锁
加锁 
     int pthread_mutex_lock(pthread_mutex_t *mutex); 
    成功返回0,失败返回错误码。如果互斥量已经被锁住,那么会导致该线程阻塞
      int pthread_mutex_trylock(pthread_mutex_t *mutex);
    成功返回0,失败返回错误码。如果互斥量已经被锁住,不会导致线程阻塞

解锁  
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    成功返回0,失败返回错误码。如果一个互斥量没有被锁住,那么解锁就会出错

四、死锁
1、死锁:线程一直在等待锁,而锁却无法解开
2、如果一个线程对已经占有的互斥量继续加锁,那么他就会陷入死锁状态。
   lock mutex--------lock mutex--------阻塞
   解除阻塞状态的条件:mutex unlock
   mutex unlock的条件:  解除阻塞态
3、程序中使用多个互斥量时,如果一个线程一直占有互斥量A,并且试图加锁互斥量B,但是拥有互斥量B的线程却要加锁互斥量A,这时就会出现死锁
    线程1: lock A成功
    线程2 : lock B成功 
    线程1: lock B失败, 阻塞
    线程2: lock A失败, 阻塞
    线程1解除阻塞的条件:线程2将B解锁
    线程2解除阻塞的条件:线程1将A解锁
4、如何去避免
    你可以小心的控制互斥量加锁的顺序来避免死锁,例如所有的线程都在加锁B之前先加锁A,那么这两个互斥量就不会产生死锁了。有的时候
    程序写的多了互斥量就难以把控,你可以先释放已经占有的锁,然后再加锁其他互斥量。
5、互斥量使用要注意:
    1)、访问共享资源时需要加锁
    2)、互斥量使用完之后需要销毁
    3)、加锁之后一定要解锁
    4)、互斥量加锁的范围要小
    5)、互斥量的数量应该少

五、手册
PTHREAD_MUTEX_DESTROY(3P)  POSIX Programmer’s Manual PTHREAD_MUTEX_DESTROY(3P)

PROLOG
       This  manual page is part of the POSIX Programmer’s Manual.  The Linux implementation of this interface may differ (con-
       sult the corresponding Linux manual page for details of Linux behavior), or the interface  may  not  be  implemented  on
       Linux.
        //这个手册只是POSIX的一部分,Linux实现的接口可能会不一样,或者有可能没有实现这个接口

NAME
       pthread_mutex_destroy, pthread_mutex_init - destroy and initialize a mutex
        //pthread_mutex_destroy, pthread_mutex_init 销毁和初始化一个互斥量

SYNOPSIS
       #include <pthread.h>
        //包含头文件

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


DESCRIPTION
       The  pthread_mutex_destroy()  function  shall destroy the mutex object referenced by mutex; the mutex object becomes, in
       effect, uninitialized. An implementation may cause pthread_mutex_destroy() to set the object referenced by mutex  to  an
       invalid value. A destroyed mutex object can be reinitialized using pthread_mutex_init(); the results of otherwise refer-
       encing the object after it has been destroyed are undefined.
        //pthread_mutex_destroy() 函数就是将一个互斥量销毁,变成一个没有经过初始化的值。这个函数的实现由可能导致互斥量
        //成为一个无效的值。一个已经被销毁的互斥量可以重新被初始化,多次销毁的结果是未知的


       It shall be safe to destroy an initialized mutex that is unlocked.  Attempting to destroy  a  locked  mutex  results  in
       undefined behavior.
        //如果一个互斥量已经初始化了,但是没有锁住,那么销毁它就是安全的操作。试图去销毁一个已经锁住的互斥量会
        //导致未知的结果

       The  pthread_mutex_init()  function shall initialize the mutex referenced by mutex with attributes specified by attr. If
       attr is NULL, the default mutex attributes are used; the effect shall be the same as passing the address  of  a  default
       mutex attributes object. Upon successful initialization, the state of the mutex becomes initialized and unlocked.
        //pthread_mutex_init()可以用指定的属性去初始化一个互斥量。如果给定的属性是空,那么会采用默认的属性。成功
        //被初始化的互斥量是没有经过加锁的

       Only  mutex  itself  may be used for performing synchronization.  The result of referring to copies of mutex in calls to
       pthread_mutex_lock(), pthread_mutex_trylock(), pthread_mutex_unlock(), and pthread_mutex_destroy() is undefined.
        //互斥量是用来同步的,重复的调用一些关于互斥量的操作,结果是未知的

       Attempting to initialize an already initialized mutex results in undefined behavior.
        //试图去初始化一个已经初始化的互斥量会导致未知的结果

       In cases where default mutex attributes are appropriate, the macro PTHREAD_MUTEX_INITIALIZER can be used  to  initialize
       mutexes  that  are  statically  allocated.  The  effect  shall  be  equivalent  to  dynamic  initialization by a call to
       pthread_mutex_init() with parameter attr specified as NULL, except that no error checks are performed.
        //可以用宏定义 PTHREAD_MUTEX_INITIALIZER 去静态的初始化一个默认属性的互斥量

RETURN VALUE
       If successful, the pthread_mutex_destroy() and pthread_mutex_init() functions shall return  zero;  otherwise,  an  error
       number shall be returned to indicate the error.
        //如果成功pthread_mutex_destroy() and pthread_mutex_init()返回0 ,失败会返回错误码

       The  [EBUSY]  and  [EINVAL]  error checks, if implemented, act as if they were performed immediately at the beginning of
       processing for the function and shall cause an error return prior to modifying the  state  of  the  mutex  specified  by
       mutex.
        //在进程的一开始就使用互斥量就会返回错误[EBUSY]

ERRORS
       The pthread_mutex_destroy() function may fail if:
        //pthread_mutex_destroy()会在一下情况下失败

       EBUSY  The implementation has detected an attempt to destroy the object referenced by mutex while it is locked or refer-
              enced (for example, while being used in a pthread_cond_timedwait() or pthread_cond_wait()) by another thread.
        //试图去销毁一个已经锁住的互斥量返回EBUSY

       EINVAL The value specified by mutex is invalid.
        //指定的互斥量无效

       The pthread_mutex_init() function shall fail if:
        //pthread_mutex_init() 会在以下情况失败

       EAGAIN The system lacked the necessary resources (other than memory) to initialize another mutex.
        //系统缺乏必要的资源(不是内存)去初始化互斥量

       ENOMEM Insufficient memory exists to initialize the mutex.
        //缺乏内存去初始化互斥量

       EPERM  The caller does not have the privilege to perform the operation.
        //调用者没有权利去执行这项操作

       The pthread_mutex_init() function may fail if:
        //pthread_mutex_init() 还有可能因为下面的情况失败

       EBUSY  The implementation has detected an attempt to reinitialize the object referenced by mutex, a previously  initial-
              ized, but not yet destroyed, mutex.
        //试图去重新初始化一个已经初始化的互斥量

       EINVAL The value specified by attr is invalid.
        //执行的属性值无效
    
       These functions shall not return an error code of [EINTR].
        //这个函数不会返回错误码EINTR

       The following sections are informative.




六、互斥量实例
1、程序框图

2、源代码

点击(此处)折叠或打开

  1. /*DATE:2015-4-1
  2.  *AUTHOR:WJ
  3.  *DESCRIPTION:    多线程访问变量产生错误的例子
  4.  */

  5. #include "apue.h"

  6. struct student{
  7.     int id;
  8.     int age;
  9.     int name;
  10. }stu;

  11. //定义两个全局变量,因为两个线程都要访问
  12. int i;
  13. pthread_mutex_t mutex;

  14. void *thread_fun1(void *arg)
  15. {
  16.     while(1)
  17.     {
  18.         //加锁,对整个结构体访问进行加锁,防止产生错乱
  19.         pthread_mutex_lock(&mutex);
  20.         stu.id = i;
  21.         stu.age = i;
  22.         stu.name = i;
  23.         i++;
  24.         if(stu.id !=stu.age || stu.id !=stu.name || stu.age!=stu.name)
  25.         {
  26.             printf("thread 1 %d,%d,%d\n", stu.id, stu.age, stu.name);
  27.             break;
  28.         }
  29.         //访问变量完成,需要进行解锁,只有这样其他线程才能访问
  30.         pthread_mutex_unlock(&mutex);
  31.     }

  32.     return(void *)0;
  33. }

  34. void *thread_fun2(void *arg)
  35. {
  36.     while(1)
  37.     {
  38.         //加锁,对整个结构体访问进行加锁,防止产生错乱
  39.         pthread_mutex_lock(&mutex);
  40.         stu.id = i;
  41.         stu.age = i;
  42.         stu.name = i;
  43.         i++;
  44.         if(stu.id!=stu.age || stu.id!=stu.name || stu.age!=stu.name)
  45.         {
  46.             printf("thread 2 %d,%d,%d\n", stu.id, stu.age, stu.name);
  47.             break;
  48.         }
  49.         pthread_mutex_unlock(&mutex);
  50.     }

  51.     return(void *)0;
  52. }

  53. int main()
  54. {
  55.     pthread_t tid1, tid2;
  56.     int err;
  57.     //对互斥量进行初始化,只有初始化过到互斥量才能使用
  58.     err = pthread_mutex_init(&mutex, NULL);
  59.     if(err != 0)
  60.     {
  61.         printf("init mutex failed\n");
  62.         return;
  63.     }
  64.     //床在新线程
  65.     err = pthread_create(&tid1, NULL, thread_fun1, NULL);
  66.     if(err != 0)
  67.     {
  68.         printf("create new thread failed\n");
  69.         return;
  70.     }
  71.     //创造新线程
  72.     err = pthread_create(&tid2, NULL, thread_fun2, NULL);
  73.     if(err != 0)
  74.     {
  75.         printf("create new thread failed\n");
  76.         return;
  77.     }
  78.     //等待新线程运行结束
  79.     pthread_join(tid1, NULL);
  80.     pthread_join(tid2, NULL);

  81.     return 0;
  82. }

作业:使用多线程对一个队列进行增加和减少,增加操作是一个线程,删除操作是一个线程

点击(此处)折叠或打开

  1. /*DATA:            2015-4-5
  2.  *AUTHOR;        WJ
  3.  *DESCRIPTION:    将文件1.c 拷贝到11.c 12.c 13.c
  4.  *    
  5.  */
  6. #include "apue.h"

  7. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  8. struct queue{
  9.     int len;
  10.     int write_pos;
  11.     int read_pos;
  12.     int data[50];
  13. };

  14. struct queue *queue_init()
  15. {
  16.     struct queue *que;
  17.     //申请内存
  18.     que = (struct queue *)malloc(sizeof(struct queue));
  19.     if(que ==NULL)
  20.     {
  21.         printf("malloc failed\n");
  22.         return;
  23.     }

  24.     //初始化
  25.     que->len = 0;
  26.     que->write_pos = 0;
  27.     que->read_pos = 0;

  28.     return que;
  29. }

  30. void queue_destroy(struct queue *que)
  31. {
  32.     //销毁互斥量和que
  33.     pthread_mutex_destroy(&mutex);
  34.     free(que);
  35. }

  36. void *queue_add(void *arg)
  37. {
  38.     struct queue *que = (struct queue *)arg;
  39.     int buf=0;
  40.     while(buf<50)
  41.     {
  42.         pthread_mutex_lock(&mutex);
  43.         que->data[que->write_pos] = buf;
  44.         que->write_pos ++;
  45.         que->len ++;
  46.         buf++;
  47.         printf("write data %d to queue\n", que->data[que->write_pos -1]);

  48.         pthread_mutex_unlock(&mutex);
  49.         sleep(1);
  50.     }
  51. }

  52. void *queue_del(void *arg)
  53. {
  54.     struct queue *que = (struct queue *)arg;
  55.     int buf=0;
  56.     while(1)
  57.     {
  58.         sleep(2);
  59.         pthread_mutex_lock(&mutex);
  60.         buf = que->data[que->read_pos];
  61.         que->read_pos ++;
  62.         if(que->len -- == 0)
  63.         {
  64.             printf("queue is empty\n");
  65.             return;
  66.         }
  67.         buf++;
  68.         printf("read data %d from queue\n", que->data[que->read_pos -1]);
  69.         pthread_mutex_unlock(&mutex);
  70.     }
  71. }

  72. int main()
  73. {
  74.     pthread_t tid1, tid2;
  75.     int err;
  76.     struct queue *que;

  77.     //队列和锁都要初始化
  78.     que = queue_init();
  79.     err = pthread_mutex_init(&mutex, NULL);
  80.     if(err)
  81.     {
  82.         printf("mutex init failed\n");
  83.         free(que);
  84.         return;
  85.     }

  86.     err = pthread_create(&tid1, NULL, queue_add, (void *)que);
  87.     if(err)
  88.     {
  89.         printf("create add thread failed\n");
  90.         queue_destroy(que);
  91.         return;
  92.     }

  93.     err = pthread_create(&tid2, NULL, queue_del, (void *)que);
  94.     if(err)
  95.     {
  96.         printf("create del thread failed\n");
  97.         queue_destroy(que);
  98.         return;
  99.     }

  100.     //等待增加和删除操作完成
  101.     pthread_join(tid1, NULL);
  102.     pthread_join(tid2, NULL);

  103.     //销毁
  104.     queue_destroy(que);
  105. }