【转】pthread_mutex_t 和 pthread_cond_t 配合使用的简要分析

参考书:《UNIX网络编程 卷2:进程间通信》 第7章  互斥锁和条件变量

原文地址:https://blog.csdn.net/chengonghao/article/details/51779279

1.原理

假设有两个线程同时访问一个全局变量 n,这个全局变量的初始值等于0。

Int  n = 0 ;

         消费者线程 A 进入临界区,访问 n,A 必须等到 n 大于 0 才能接着往下执行,如果 n== 0,那么 A 将一直等待。

         还有一个生产者线程 B,B 进入临界区,修改 n 的值,使得 n >0,当 n > 0 时,B 通知等待 n > 0 的消费者线程A。A 被 B 通知之后就可以接着往下执行了。

 


         以上情况造成死锁:

         当 A 进入临界区时,其他线程不能进入临界区,意味着 B 没有机会去修改 n, n 的值一直为 0,不满足A 继续执行的条件(n > 0),A 只能一直等待。

         消费者进程拿到互斥锁 --> 进入临界区 --> 发现共享资源 n 不满足继续执行的条件(n> 0) --> 等待 n > 0

         消费者进程占有互斥锁 --> 生产者进程无法进入临界区 --> 无法修改 n 的值 --> 生产者等待消费者释放互斥锁

         解决死锁的方案就是采用条件变量。

         通常情况下,对共享资源(比如 n)保护要用到锁操作,当一个进程进入临界区时会拿到互斥锁(lock 操作),然后其他进程拿不到互斥锁,也就无法进入临界区,因此当进程进入临界区,发现共享资源不满足继续向下执行的条件(n > 0)时,就应该释放锁,让其他进程修改共享资源,以满足自己所需的执行条件。

消费者进入临界区 --> 共享变量不满足继续向下执行的条件 --> 消费者等待在条件变量 --> 释放互斥锁 --> 生产者进入临界区 --> 修改条件变量 --> 生产者通知消费者:现在有多的资源了,快来使用 --> 消费者再次拿互斥锁 --> 消费资源 --> 释放互斥锁。如果有多个消费者进程等待在条件变量上,就可以形成等待队列。

生产者和消费者模型中互斥锁条件变量的使用流程图如下,其中蓝色代表消费者的执行流,红色是生产者的执行流。

 

 

2.使用方法

         条件变量的使用主要有以下五个函数:

[cpp]  view plain  copy
  1. /* 初始化一个条件变量 */  
  2. int pthread_cond_init (pthread_cond_t* cond, pthread_condattr_t *cond_attr);  
  3.    
  4. /* 销毁一个条件变量 */  
  5. int pthread_cond_destroy(pthread_cond_t* cond);  
  6.    
  7. /* 令一个消费者等待在条件变量上 */  
  8. int pthread_cond_destroy(pthread_cond_t* cond);  
  9.    
  10. /* 生产者通知等待在条件变量上的消费者 */  
  11. int pthread_cond_signal(pthread_cond_t* cond);  
  12.    
  13. /* 生产者向消费者广播消息 */  
  14. int pthread_cond_broadcast(pthread_cond_t* cond);  


        

 

消费者等待条件的伪代码:

[cpp]  view plain  copy
  1. pthread_mutex_lock(&mutex); // 拿到互斥锁,进入临界区  
  2. while( 条件为假)  
  3.     pthread_cond_wait(cond, mutex); // 令进程等待在条件变量上  
  4. 修改条件  
  5. pthread_mutex_unlock(&mutex); // 释放互斥锁  


 

生产者通知消费者的伪代码:

[cpp]  view plain  copy
  1. pthread_mutex_lock(&mutex); // 拿到互斥锁,进入临界区  
  2. 设置条件为真  
  3. pthread_cond_signal(cond); // 通知等待在条件变量上的消费者  
  4. pthread_mutex_unlock(&mutex); // 释放互斥锁  


 

以下是示例程序,演示了互斥锁和条件变量配合使用方法,由于是在Linux下写的程序,所以注释全是英文的。

condition_test.c:

[cpp]  view plain  copy
  1. /*************************************************************** 
  2. *  Copyright (C) 2016 chengonghao 
  3. *  All rights reserved. 
  4. * 
  5. *  chengonghao@yeah.net 
  6. ***************************************************************/  
  7. #include <unistd.h>  
  8. #include <pthread.h>  
  9.   
  10. #define CONSUMERS_COUNT 2     
  11. #define PRODUCERS_COUNT 1  
  12.   
  13. pthread_mutex_t g_mutex ;  
  14. pthread_cond_t g_cond ;  
  15.   
  16. pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT] ;  
  17. int share_variable = 0 ;// this is the share variable, shared by consumer and producer  
  18.   
  19. void* consumer( void* arg )  
  20. {  
  21.    int num = (int)arg ;  
  22.    while ( 1 )   
  23.    {  
  24.       /******* critical section begin *******/  
  25.       pthread_mutex_lock( &g_mutex ) ;  
  26.   
  27.       // if share_variable == 0, means consumer shell stop here  
  28.       while ( share_variable == 0 )  
  29.       {  
  30.          printf( "consumer %d begin wait a condition...\n", num ) ;  
  31.          // put a thread blocked ont a condition variable( here is g_cond),  
  32.          // and unlock the mutex( here is g_mutex )  
  33.          pthread_cond_wait( &g_cond, &g_mutex ) ;  
  34.       }  
  35.       // here means n != 0 and consumer can goes on  
  36.       // consumer consumed shared variable, so the number of shared variable shell minus  
  37.       printf( "consumer %d end wait a condition...\n", num ) ;  
  38.       printf( "consumer %d begin consume product\n", num ) ;  
  39.       -- share_variable ;  
  40.   
  41.       pthread_mutex_unlock( &g_mutex ) ;  
  42.       /******** critial section end *********/  
  43.       sleep( 1 ) ;  
  44.    }  
  45.      
  46.    return NULL ;  
  47. }  
  48.   
  49. void* producer( void* arg )  
  50. {  
  51.    int num = (int)arg ;  
  52.    while ( 1 )  
  53.    {  
  54.       /******* critical section begin *******/  
  55.       pthread_mutex_lock( &g_mutex ) ;  
  56.   
  57.       // produce a shared variable  
  58.       printf( "producer %d begin produce product...\n", num ) ;  
  59.       ++ share_variable ;  
  60.       printf( "producer %d end produce product...\n", num ) ;  
  61.       // unblock threads blocked on a condition variable( here is g_cond )  
  62.       pthread_cond_signal( &g_cond ) ;  
  63.       printf( "producer %d notified consumer by condition variable...\n", num ) ;  
  64.       pthread_mutex_unlock( &g_mutex ) ;  
  65.   
  66.       /******** critial section end *********/  
  67.       sleep( 5 ) ;  
  68.    }  
  69.      
  70.    return 1 ;  
  71. }  
  72.   
  73.   
  74. int main( void )  
  75. {  
  76.    // initiate mutex  
  77.    pthread_mutex_init( &g_mutex, NULL ) ;  
  78.    // initiate condition  
  79.    pthread_cond_init( &g_cond, NULL ) ;  
  80.   
  81.    // initiate consumer threads  
  82.    for ( int i = 0; i < CONSUMERS_COUNT; ++ i )  
  83.    {  
  84.       pthread_create( &g_thread[i], NULL, consumer, (void*)i ) ;  
  85.    }  
  86.    sleep( 1 ) ;  
  87.    // initiate producer threads  
  88.    for ( int i = 0; i < PRODUCERS_COUNT; ++ i )  
  89.    {  
  90.       pthread_create( &g_thread[i], NULL, producer, (void*)i ) ;  
  91.    }  
  92.    for ( int i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; ++ i )   
  93.    {  
  94.       pthread_join( g_thread[i], NULL ) ;  
  95.    }  
  96.   
  97.    pthread_mutex_destroy( &g_mutex ) ;  
  98.    pthread_cond_destroy( &g_cond ) ;  
  99. }  


 

编译程序:

[cpp]  view plain  copy
  1. cgh@ubuntu:~/condition_test$ gcc condition_test.c -o test –lpthread  


运行程序:


1.      第一个框,消费者 1 和0 发现share_variable == 0,于是先后等待在条件变量上;

2.      第二个框,生产者 0 开始生产共享变量,即 ++ share_variable,然后通知等待在条件变量上的消费者;

3.      第三个框,消费者 1 被生产者唤醒,开始消费共享变量,即– share_variable;

4.      第四个框,生产者 0 继续生产共享变量,++ share_variable,然后通知等待在条件变量上的消费者;

5.      第五个框,消费者 0 被唤醒,开始消费共享变量,即– share_variable;

以此类推,以上描述简化了拿锁和释放锁的过程,可以结合上面的流程图来理解代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值