• POSIX 使用互斥量和条件变量实现生产者/消费者问题


       boost的mutex,condition_variable非常好用。但是在Linux上,boost实际上做的是对pthread_mutex_t 和pthread_cond_t的一系列的封装。因此通过对原生态的POSIX 的mutex,cond的生成者,消费者的实现,我们可以再次体会boost带给我们的便利。

    1. 什么是互斥量

           互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线 程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所以在该互斥锁上的阻塞线程都会变成可进行状态,第一个变成运行状态的线程可以 对互斥量加锁,其他线程在次被阻塞,等待下次运行状态。

    pthread_mutex_t 就是POSIX对于mutex的实现。

    函数名 参数 说明
    pthread_mutex_init

    pthread_mutex_t * mutex,

    constpthread_mutex_t *attr
    初始化一个互斥量,静态方式可以直接使用PTHREAD_MUTEX_INITIALIZER进行赋值初始化
    pthread_mutex_destroy pthread_mutex_t *mutex 释放对互斥变量分配的资源。注意pthread_mutex_init有可能malloc了资源
    pthread_mutex_lock pthread_mutex_t *mutex 如果互斥量已经上锁,调用线程阻塞直至互斥量解锁
    pthread_mutex_trylock pthread_mutex_t *mutex 加锁,如果失败不阻塞
    pthread_mutex_unlock pthread_mutex_t *mutex 解锁

    使用init函数进行初始化:

    1. #include <pthread.h>  
    2.   
    3. pthread_mutex_t foo_mutex;  
    4.   
    5. void foo()  
    6. {  
    7.   pthread_mutex_init(&foo_mutex, NULL);  
    8.   pthread_mutex_lock(&foo_mutex);  
    9.   /* Do work. */  
    10.   pthread_mutex_unlock(&foo_mutex);  
    11.   pthread_mutex_destroy(&foo_mutex);  
    12. }  

    当然该初始化

    1. pthread_mutex_init(&foo_mutex, NULL);  


    只能foo_mutex使用前初始化一次,最后destroy。初始化已经初始化的mutex将导致undefined behavior。

    另外一种用法:

    1. pthread_mutex_t foo_mutex = PTHREAD_MUTEX_INITIALIZER;  
    2. void foo()  
    3. {  
    4.   pthread_mutex_lock(&foo_mutex);  
    5.   /* Do work. */  
    6.   pthread_mutex_unlock(&foo_mutex);  
    7. }  


    当 然了,这两种用法都有问题:如果在lock住后unlock之前出现exception,那么这个锁永远也不能unlock。这种情况下需要guard这 个资源。具体可参照boost::mutex::scoped_lock的实现,非常简单但是极大简化了mutex的安全使用。

    2. 什么是条件变量

          与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。

          条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。

         条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条 件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

          条件变量的初始化和mutex的初始化差不多,也是有两种方式:

              pthread_cond_tmy_condition=PTHREAD_COND_INITIALIZER;

              也可以利用函数pthread_cond_init动态初始化。

    下面中各个函数的简介。

    函数名 参数 说明
    pthread_cond_init pthread_cond_t *cond,
    const pthread_condattr_t *attr
    初始化
    pthread_cond_destroy pthread_cond_t *cond 回收
    pthread_cond_wait pthread_cond_t *cond,
    pthread_mutex_t *mutex
    等待,无超时
    pthread_cond_timedwait pthread_cond_t *cond,pthread_mutex_t *mutex,
    const struct timespec *abstime
    等待,有超时
    pthread_cond_signal pthread_cond_t *cond 一个在相同条件变量上阻塞的线程将被解锁。如果同时有多个线程阻塞,则由调度策略确定接收通知的线程
    pthread_cond_broadcast pthread_cond_t *cond 将通知阻塞在这个条件变量上的所有线程。一旦被唤醒,线程仍然会要求互斥锁。


    一个简单使用条件变量进行线程同步的小例子:

    1. #include <pthread.h>  
    2. #include <stdio.h>  
    3. #include <stdlib.h>  
    4.   
    5. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
    6. pthread_cond_t cond = PTHREAD_COND_INITIALIZER;  
    7.   
    8. void *thread1(void *);  
    9. void *thread2(void *);  
    10.   
    11. int i=1;  
    12. int main(void)  
    13. {  
    14.     pthread_t t_a;  
    15.     pthread_t t_b;  
    16.   
    17.     pthread_create(&t_a,NULL,thread2,(void *)NULL);/*create thread t_a*/  
    18.     pthread_create(&t_b,NULL,thread1,(void *)NULL); /*create thread t_b*/  
    19.     pthread_join(t_b, NULL);/*wait for exit of t_b*/  
    20.     pthread_join(t_a, NULL);  
    21.     pthread_mutex_destroy(&mutex);  
    22.     pthread_cond_destroy(&cond);  
    23.     exit(0);  
    24. }  
    25.   
    26. void *thread1(void *junk)  
    27. {  
    28.     for(i=1;i<=9;i++)  
    29.     {  
    30.         pthread_mutex_lock(&mutex);  
    31.         if(i%3==0)  
    32.              pthread_cond_signal(&cond);  
    33.         else  
    34.              printf("thread1 running, i = %d ",i);  
    35.         pthread_mutex_unlock(&mutex);  
    36.         sleep(1);  
    37.     }  
    38. }  
    39.   
    40.   
    41. void *thread2(void *junk)  
    42. {  
    43.     while(i<9)  
    44.     {  
    45.         pthread_mutex_lock(&mutex);  
    46.         if(i%3!=0)  
    47.             pthread_cond_wait(&cond,&mutex);/*..*/  
    48.         printf("thread2 running, i = %d ",i);  
    49.         pthread_mutex_unlock(&mutex);  
    50.         sleep(1);  
    51.     }  
    52. }   


    输出:

    1. thread1 running, i = 1  
    2. thread1 running, i = 2  
    3. thread2 running, i = 3  
    4. thread1 running, i = 4  
    5. thread1 running, i = 5  
    6. thread2 running, i = 6  
    7. thread1 running, i = 7  
    8. thread1 running, i = 8  
    9. thread2 running, i = 9  

    3. 生产者-消费者的实现

            生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的 线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消 费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。    

    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #define MAX 5  
    4.   
    5. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /*初始化互斥锁*/  
    6. pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化条件变量*/  
    7.   
    8. typedef struct{  
    9.   char buffer[MAX];  
    10.   int how_many;  
    11. }BUFFER;  
    12.   
    13. BUFFER share={"",0};  
    14. char ch='A';/*初始化ch*/  
    15.   
    16. void *producer(void *);  
    17. void *consumer(void *);  
    18.   
    19. int main(void)  
    20. {  
    21.   pthread_t t_read;  
    22.   pthread_t t_write;  
    23.   
    24.   pthread_create(&t_write,NULL,producer,(void *)NULL); /*创建进程t_a*/  
    25.   pthread_create(&t_read,NULL,consumer,(void *)NULL); /*创建进程t_b*/  
    26.   pthread_join(t_write,(void **)NULL);  
    27.   pthread_join(t_read, NULL);  
    28.   exit(0);  
    29. }  
    30.   
    31. void *producer(void *junk)  
    32. {  
    33.   int n=0;  
    34.   
    35.   printf("Producer: starting ");  
    36.   
    37.   while(ch!='K')  
    38.   {  
    39.       pthread_mutex_lock(&mutex);/*锁住互斥量*/  
    40.       if(share.how_many!=MAX)  
    41.       {  
    42.           share.buffer[share.how_many++]=ch++;/*把字母写入缓存*/  
    43.           printf("Producer: put char[%c] ",ch-1);/*打印写入字母*/  
    44.           if(share.how_many==MAX)  
    45.           {  
    46.                printf("Producer: signaling full ");  
    47.                pthread_cond_signal(&cond);/*如果缓存中的字母到达了最大值就发送信号*/  
    48.           }  
    49.       }  
    50.       pthread_mutex_unlock(&mutex);/*解锁互斥量*/  
    51.   }  
    52.   sleep(1);  
    53.   printf("Producer:Exiting ");  
    54.   return NULL;  
    55. }  
    56.   
    57. void *consumer(void *junk)  
    58. {  
    59.   int i;  
    60.   int n=0;  
    61.   printf("Consumer: starting ");  
    62.   
    63.   while(ch!='K')  
    64.   {  
    65.       pthread_mutex_lock(&mutex);/*锁住互斥量*/  
    66.       printf(" Consumer : Waiting ");  
    67.       while(share.how_many!=MAX)/*如果缓存区字母不等于最大值就等待*/  
    68.           pthread_cond_wait(&cond,&mutex);  
    69.       printf("Consumer: getting buffer:: ");  
    70.       for(i=0;share.buffer[i]&&share.how_many;++i,share.how_many--)  
    71.           putchar(share.buffer[i]); /*循环输出缓存区字母*/  
    72.       putchar(' ');  
    73.       pthread_mutex_unlock(&mutex);/*解锁互斥量*/  
    74.   }  
    75.   printf("Consumer: exiting ");  
    76.   return NULL;  
    77. }  
    78.    

    输出:Producer: starting
    Producer: put char[A]
    Producer: put char[B]
    Producer: put char[C]
    Producer: put char[D]
    Producer: put char[E]
    Producer: signaling full
    Consumer: starting

    Consumer : Waiting
    Consumer: getting buffer:: ABCDE

    Consumer : Waiting
    Producer: put char[F]
    Producer: put char[G]
    Producer: put char[H]
    Producer: put char[I]
    Producer: put char[J]
    Producer: signaling full
    Consumer: getting buffer:: FGHIJ
    Consumer: exiting
    Producer:Exiting

    来源:http://blog.csdn.net/anzhsoft/article/details/19044069

             http://blog.csdn.net/anzhsoft2008/article/category/2123999

  • 相关阅读:
    LaTeX公式编辑器
    早期和东京,京都大学高考试题
    猎犬追狐狸试题
    矩阵方程的计算求解(Matlab实现)
    高考压轴题
    何天成:从高联到IMO金牌,超详细数学竞赛学习方法
    几个精彩的数论问题
    高考试题网站
    泛函分析有什么好的教材?
    ifndef系列
  • 原文地址:https://www.cnblogs.com/sunminmin/p/4481535.html
Copyright © 2020-2023  润新知