Linux--Condition Variable(條件變量)實現生產者-消費者模型 、讀寫鎖

一、條件變量

    在線程同步過程中還有如下的情況:線程A需要等某個條件成立之後才能繼續往下執行,如果條件不成立,線程A就阻塞,而線程B在執行過程中使這個條件成立了,就喚醒線程A繼續執行。在Pthread庫中用條件變量阻塞等待一個條件,或者喚醒等待這個條件的線程。條件變量用pthread_cond_t類型的變量來表示。

    用pthread_cond_init 初始化條件變量、如果條件變量是靜態分配的,也可以用宏定義          PTHEAD_COND_INITIALIZER初始化,用pthread_cond_destroy 銷燬條件變量;成功返回0,失敗返回錯誤號。

     一個條件變量總是和一個Mutex搭配使用的。一個線程可以調用pthread_cond_wait在一個條件變量上阻塞等待,這個函數做以下三步操作:

    1. 釋放Mutex

    2. 阻塞等待

    3. 當被喚醒時,重新獲得Mutex並返回

   一個線程可以調用pthread_cond_signal喚醒在某個條件變量上等待的另一個線程,也可以調用pthread_cond_broadcast喚醒在這個條件變量上等待的所有線程。

二、用生產者-消費者模型來說明

    顧名思義,可以看出要實現這個模型,首先得有兩個角色(生產者,消費者),有了兩個角色之外當然該得有一個場合讓兩個都能訪問到的臨界資源(一個場合),還得弄明白生產者與生產者之間的關係(互斥),消費者與消費者之間的關係(互斥),生產者和消費者之間的關係(同步與互斥),總的來說就是一個場所,兩個角色,三種關係。用代碼來實現,生產者生產一個數據,然後發出信號讓,消費者消費,消費者消費完之後給生產者發信號告訴生產者讓生產者繼續生產,如此重複。

    

  1 #include<stdio.h>
  2 #include <stdlib.h>
  3 #include<malloc.h>
  4 #include<pthread.h>                                                                                                                             
  5 #include<semaphore.h>
  6 typedef int Data_type;
  7 typedef int* Data_type_p;
  8 static pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//初始化互斥鎖
  9 static pthread_cond_t needProduct=PTHREAD_COND_INITIALIZER;//初始化條件變量
 10 
 11
 12 typedef struct listnode //定義一個鏈表來存放數據(一個場所)
 13 {
 14     Data_type data;
 15     struct listnode* next;
 16 }list ,*listp,**listpp;
 17 
 18 listp head=NULL;
 19 
 20 static listp buyNode(Data_type _data)
 21 {
 22     listp tem=(listp)malloc(sizeof(list));
 23     if(tem)
 24     {
 25         tem -> data=_data;
 26         tem -> next=NULL;
 27         return tem;
 28     }
 29     return NULL;
 30 }
 31 void initList(listpp list)
 32 {
 33     *list=buyNode(0);
 34 }
 35 void push_list(listp list,Data_type _data)
 36 {
 37     listp cur=buyNode(_data);
 38     listp tem=list;
 39     while(tem->next)
 40     {
 41         tem=tem->next;
 42     }
 43     tem ->next=cur;
 44 }
 45 void deleteList(listp list)
 46 {
 47     if(list)
 48     {
 49         free(list);
 50         list=NULL;
 51     }
 52 }
 53 int pop_list(listp list,Data_type_p data)
 54 {
 55     if(list ->next==NULL)
 56     {
 57         *data =-1;
 58         return -1;
 59     }
 60     listp tem=list->next;
 61     list ->next=tem->next;
 62     *data=tem->data;
 63     deleteList(tem);
 64     return 0;
 65 }
 66 void PrintList(listp list)
 67 {
 68     listp cur=list->next;;
 69     while(cur)
 70     {
 71         printf("%d",cur->data);
 72         fflush(stdout);
 73         cur=cur->next;
 74     }
 75     printf("\n");
 76 }
 77 void *product(void* arg)//定義生產者與生產者之間的關係(互斥)
 78 {
 79     int i=0;
 80     while(1)
 81     {
 82         pthread_mutex_lock(&lock);
 83         printf("product data:%d\n",i);
 84         push_list(head,i++);
 85         pthread_mutex_unlock(&lock);
 86         printf("conduct is ok.weak up comsumer...\n");
 87         pthread_cond_signal(&needProduct);//當生產者有數據時,發送信號,喚醒消費者
 88         sleep(2);
 89     }
 90 
 91 }
 92 void *consumer(void* arg)//消費者與消費者之間的關係(互斥)
 93 {
 94     Data_type _data;
 95     while(1)
 96     {   
 97         pthread_mutex_lock(&lock);
 98         while(-1==pop_list(head,&_data))
 99         {
100             pthread_cond_wait(&needProduct,&lock);//沒收到生產者的消息之前就阻塞等待
101         }
102         printf("consumer data:%d\n",_data);
103         pthread_mutex_unlock(&lock);
104         sleep(1);
105     }
106 }
107 int main()
108 {
109     initList(&head);
110     pthread_t id1;
111     pthread_t id2;
112     pthread_create(&id1,NULL,product,NULL);
113     pthread_create(&id2,NULL,consumer,NULL);
114     pthread_join(id1,NULL);
115     pthread_join(id2,NULL);
116     return 0;
117 }

    總結:上面代碼實現的是單生產者和單消費者,生產者-消費者模型,簡單的來說就是要實現生產者與生產者之間互斥,消費者與消費者之間互斥,生產者與消費者之間同步互斥的關係。


三、用信號量實現生產者-消費者模型

    Mutex變量是非0即1的,可看作一種資源的可用數量,初始化時Mutex是1,表示有一個可用資源,

加鎖時獲得該資源,將Mutex減到0,表示不再有可用資源,解鎖時釋放該資源,將Mutex重新加到1,表示又有了一個可用資源。信號量(Semaphore)和Mutex類似,表示可用資源的數量,和Mutex不同的是這個數量可以大於1。即,如果信號量描述的資源數目是1時,此時的信號量和互斥鎖相同!

    sem_init()初始化信號量

    sem_wait()P操作獲得資源

    sem_post()V操作釋放資源

    sem_destroy()銷燬信號量

    上面是用鏈表寫的生產者-消費者模型,其空間是動態分配的,現在基於固定大小的環形隊列重寫生產者-消費者模型

  1 #include<stdio.h>
  2 #include<pthread.h>
  3 #include<semaphore.h>
  4 #define PRODUCT_SIZE 20
  5 #define CONSUMER_SIZE 0
  6 
  7 sem_t produceSem;
  8 sem_t consumerSem;
  9 int Blank [PRODUCT_SIZE];
 10 
 11 void* product(void* arg)
 12 {
 13     int p=0;
 14     while(1)
 15     {
 16         sem_wait(&produceSem); //申請資源。
 17         int _product=rand()%100;
 18         Blank[p]=_product;
 19         printf("product is ok ,value is :%d\n",_product);
 20         sem_post(&consumerSem);//釋放資源
 21         p=(p+1) % PRODUCT_SIZE;
 22         sleep(rand()%3);
 23     }
 24 
 25 }
 26 void* consumer(void* arg)
 27 {
 28     int p=0;
 29     while(1)
 30     {
 31         sem_wait(&consumerSem);//申請資源
 32         int _consumer=Blank[p];
 33         printf("consumer is ok,value is :%d\n",_consumer);
 34         sem_post(&produceSem);//釋放資源
 35         p=(p+1)% PRODUCT_SIZE;
 36         sleep(rand()%5);
 37     }
 38 }
 39 int main()
 40 {   sem_init(&produceSem,0,PRODUCT_SIZE);
 41     sem_init(&consumerSem,0,CONSUMER_SIZE);
 42     pthread_t tid1,tid2;
 43     pthread_create(&tid1,NULL,product,NULL);
 44     pthread_create(&tid2,NULL,consumer,NULL);
 45     pthread_join(tid1,NULL);
 46     pthread_join(tid2,NULL);
 47     sem_destroy(&produceSem);
 48     sem_destroy(&consumerSem);
 49     return 0;
 50 }

四、讀寫鎖  

    讀寫鎖實際是一種特殊的自旋鎖,用來處理讀多寫少的情況,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源進行讀訪問,寫者則需要對共享資源進行寫操作。這種鎖相對於自旋鎖而言,能提高併發性,因爲在多處理器系統中,它允許同時有多個讀者來訪問共享資源,最大可能的讀者數爲實際的邏輯CPU數。寫者是排他性的,一個讀寫鎖同時只能有一個寫者或多個讀者(與CPU數相關),但不能同時既有讀者又有寫者。讀寫鎖也遵循3種關係:讀者-寫者(互斥與同步)、讀者-讀者(無關係)、寫者-寫者(互斥)2個對象(讀者和寫者),1個場所。

    pthread_rwlock_wrlock 寫方式,成功返回0,失敗返回錯誤碼

    pthread_rwlock_rdlock 讀方式,成功返回0,失敗返回錯誤碼

    pthread_rwlock_init初始化

  1 #include<stdio.h>
  2 #include<pthread.h>
  3 #define _READNUM_ 2
  4 #define _WREITENUM_ 3
  5 pthread_rwlock_t lock;
  6 int buf=0;
  7 void* read(void* reg)
  8 {
  9     while(1)
 10     {
 11         if(pthread_rwlock_tryrdlock(&lock) != 0)//讀方式
 12         {
 13             printf("writer is write! reader wait...\n");
 14         }
 15         else
 16         {
 17             printf("reader is reading,val is %d\n",buf);
 18             pthread_rwlock_unlock(&lock);
 19         }
 20         sleep(2);
 21     }
 22 }
 23 void* write(void* reg)
 24 {
 25     while(1)
 26     {
 27         if(pthread_rwlock_trywrlock(&lock) != 0)//寫方式
 28         {
 29             printf("reader is reading ! writer wait...\n");
 30             sleep(1);
 31         }
 32         else
 33         {
 34             buf++;
 35             printf("writer is writing,val is %d\n",buf);
 36             pthread_rwlock_unlock(&lock);
 37 
 38         }
 39         sleep(1);
 40     }
 41 }
 42 int main()
 43 {
 44     pthread_rwlock_init(&lock,NULL);
 45     pthread_t tid;
 46     int i=0;
 47     for(i;i< _WREITENUM_;i++)
 48     {
 49         pthread_create(&tid,NULL,write,NULL);
 50     }
 51 
 52     for(i; i< _READNUM_;i++)
 53     {
 54         pthread_create(&tid,NULL,read,NULL);
 55     }
 56         pthread_join(tid,NULL);
 57         sleep(100);
 58         return 0;
 59 }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章