二、(LINUX 執行緒同步) 互斥量、條件變數以及生產者消費者問題

gaopengtttt發表於2017-05-20
原創轉載請註明出處:

接上一篇:

一、(LINUX 執行緒同步) 引入  http://blog.itpub.net/7728585/viewspace-2137980/


線上程同步中我們經常會使用到mutex互斥量,其作用用於保護一塊臨界區,避免多執行緒併發操作對這片臨界區帶來的資料混亂,
POSIX的互斥量是一種建議鎖,因為如果不使用互斥量也可以訪問共享資料,但是可能是不安全的。
其原語包含:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
靜態初始一個互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);
銷燬一個互斥量
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
初始化一個互斥量
int pthread_mutex_lock(pthread_mutex_t *mutex);
互斥量加鎖操作,在這個函式呼叫下的臨界區只允許一個執行緒進行訪問,如果不能獲得鎖則堵塞等待
int pthread_mutex_trylock(pthread_mutex_t *mutex);
互斥量加鎖操作,在這個函式呼叫下的臨界區只允許一個執行緒進行訪問,如果獲得不了鎖則放棄
int pthread_mutex_unlock(pthread_mutex_t *mutex);
互斥量解鎖操作,一般用於在臨界區資料操作完成後解鎖


而條件變數cond則代表當某個條件不滿足的情況下,本執行緒應該放棄鎖,並且將本執行緒堵塞。典型的
生產者消費者問題,如果生產者還沒來得及生產東西,消費者則不應該進行消費操作,應該放棄鎖,將
自己堵塞,直到條件滿足被生產者喚醒。原語包含:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
靜態初始一個條件變數
int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *cattr);
初始化一個條件變數
int pthread_cond_destroy(pthread_cond_t *cond)
銷燬一個條件變數量
int   pthread_cond_wait(pthread_cond_t   *cond,   pthread_mutex_t   *mutex)   
由於某種條件不滿足,解鎖和他繫結互斥量,本執行緒堵塞等待條件成熟被其他執行緒喚醒,如消費者等待生產者生成完成後被喚醒
注意這裡就繫結了一個互斥量,也就是說條件變數一般和某個互斥量配套使用,因為單獨的條件變數達不到任何堵塞執行緒的目的
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
和上面一樣,只是加入了堵塞的時間
int pthread_cond_signal(pthread_cond_t *cond)
條件滿足喚醒由於wait在這個條件變數上某個執行緒,一般用於生產者喚醒消費者
int pthread_cond_broadcast(pthread_cond_t *cond)
條件滿足喚醒由於wait在這個條件變數上全部執行緒,一般用於生產者喚醒消費者

下面一張自己畫的圖希望對大家有所幫助。


最後是我寫的一個,用一個連結串列棧結構,來模擬一個生產者生產資料,N個消費者併發進行消費的
一個生產者消費者程式。生產者一次生產一批資料,消費者一次只消費一個資料。
當生產者一次生產10個資料的時候,輸出是這樣的。
p:thread:140034536785664 prod 1 data is:data A:0 data B:0
p:thread:140034536785664 prod 2 data is:data A:1 data B:1
p:thread:140034536785664 prod 3 data is:data A:2 data B:2
p:thread:140034536785664 prod 4 data is:data A:3 data B:3
p:thread:140034536785664 prod 5 data is:data A:4 data B:4
p:thread:140034536785664 prod 6 data is:data A:5 data B:5
p:thread:140034536785664 prod 7 data is:data A:6 data B:6
p:thread:140034536785664 prod 8 data is:data A:7 data B:7
p:thread:140034536785664 prod 9 data is:data A:8 data B:8
p:thread:140034536785664 prod 10 data is:data A:9 data B:9
c:thread:140034520000256 cost 10 data is:data A:9 data B:9
c:thread:140034520000256 cost 9 data is:data A:8 data B:8
c:thread:140034520000256 cost 8 data is:data A:7 data B:7
c:thread:140034520000256 cost 7 data is:data A:6 data B:6
c:thread:140034520000256 cost 6 data is:data A:5 data B:5
c:thread:140034520000256 cost 5 data is:data A:4 data B:4
c:thread:140034520000256 cost 4 data is:data A:3 data B:3
c:thread:140034520000256 cost 3 data is:data A:2 data B:2
c:thread:140034520000256 cost 2 data is:data A:1 data B:1
c:thread:140034520000256 cost 1 data is:data A:0 data B:0
也可以看到他確實是一個後進先出的棧模型
這個時候並沒有觀察到多個執行緒競爭的問題,如果我將生產者定義為一次生產10000個資料,就可以
看到多個執行緒交替使用生產者資料的情況。
c:thread:140270541522688 cost 9959 data is:data A:9958 data B:9958 c:thread:140270667347712 cost 9952 data is:data A:9951 data B:9951
我們可以看到不同的執行緒ID的執行緒在競爭這把鎖
下面是程式碼,臨界區應該儘量選擇小,我這裡臨界區選擇較大,比如連結串列的建立
c:thread:140270692525824 cost 9962 data is:data A:9961 data B:9961
c:thread:140270692525824 cost 9961 data is:data A:9960 data B:9960
c:thread:140270692525824 cost 9960 data is:data A:9959 data B:9959
c:thread:140270541522688 cost 9959 data is:data A:9958 data B:9958
c:thread:140270541522688 cost 9958 data is:data A:9957 data B:9957
c:thread:140270541522688 cost 9957 data is:data A:9956 data B:9956
c:thread:140270541522688 cost 9956 data is:data A:9955 data B:9955
c:thread:140270541522688 cost 9955 data is:data A:9954 data B:9954
c:thread:140270541522688 cost 9954 data is:data A:9953 data B:9953
c:thread:140270541522688 cost 9953 data is:data A:9952 data B:9952
c:thread:140270667347712 cost 9952 data is:data A:9951 data B:9951
其實不需要放到臨界區中,還比如記憶體的delete也不需要放到臨界區,處於測試
目的已經達到,就不在糾結這個問題了:

點選(此處)摺疊或開啟

  1. 標頭檔案:
  2. /*************************************************************************
  3.   > File Name: chain.h
  4.   > Author: gaopeng QQ:22389860 all right reserved
  5.   > Mail: gaopp_200217@163.com
  6.   > Created Time: Sun 04 Jun 2017 06:27:38 AM CST
  7.  ************************************************************************/

  8. #include<iostream>
  9. #include<stdio.h>
  10. #include <stdlib.h>
  11. #include <unistd.h>

  12. #define MAX_TID 10
  13. using namespace std;


  14. class t1data
  15. {
  16.         private:
  17.                 int a;
  18.                 int b;
  19.         public:
  20.                 t1data(){}
  21.                 t1data(int i)
  22.                 {
  23.                         this->= i;
  24.                         this->= i;
  25.                 }
  26.                 virtual void prin(void)
  27.                 {
  28.                         cout<<"data A:"<<a<<" data B:"<<b;
  29.                 }

  30. };


  31. typedef struct queue_s 
  32. {
  33.         t1data data;
  34.         queue_s *next,*priv;
  35.         queue_s(int i)
  36.         {
  37.                 data = t1data(i);
  38.                 next = NULL;
  39.                 priv = NULL;
  40.         }
  41. } QUE_S,*QUE_S_P ;

  42. typedef struct queue_h
  43. {
  44.         QUE_S_P head_p;
  45.         QUE_S_P last_p;
  46.         unsigned int len;
  47.         pthread_mutex_t pmut;
  48.         pthread_cond_t pcon;
  49.         queue_h()
  50.         {
  51.                 head_p=NULL;
  52.                 last_p=NULL;
  53.                 len = 0;
  54.         }
  55. } QUE_H;

  56. int debug_return(const int ret)
  57. {
  58.         if(ret != 0)
  59.         {
  60.                 strerror(ret);
  61.                 exit(-1);
  62.         }
  63.         return 0;
  64. }
主檔案:

點選(此處)摺疊或開啟

  1. /*************************************************************************
  2.   > File Name: main.cpp
  3.   > Author: gaopeng QQ:22389860 all right reserved
  4.   > Mail: gaopp_200217@163.com
  5.   > Created Time: Sun 04 Jun 2017 07:18:28 AM CST
  6.  ************************************************************************/

  7. #include<iostream>
  8. #include<stdio.h>
  9. #include <pthread.h>
  10. #include<string.h>
  11. #include"chain.h"

  12. #define MAXDATA 10000
  13. //雙向連結串列棧結構模型

  14. using namespace std;

  15. static int COUNT = 0;


  16. void* pro(void* arg)
  17. {

  18.         QUE_H* my_head;
  19.         my_head = (QUE_H*)arg;
  20.         QUE_S_P sp =NULL;
  21.         int ret = 0;

  22.         while(1)
  23.         {
  24.                 ret = pthread_mutex_lock(&my_head->pmut);
  25.                 debug_return(ret);

  26.                 if(my_head->head_p != NULL)//如果元素沒有消費完放棄MUTEX,進入下次迴圈
  27.                 {
  28.                         //cout<<"pro head != NULL unlock\n";
  29.                         ret = pthread_mutex_unlock(&my_head->pmut);
  30.                         debug_return(ret);
  31.                         continue;
  32.                 }
  33.                 //如果消費完進行生產10000個元素
  34.                 for(;COUNT<MAXDATA;COUNT++)//生產10000個元素
  35.                 {
  36.                         if(my_head->head_p == NULL)
  37.                         {
  38.                                 int tm = 0;
  39.                                 QUE_S_P sp = new QUE_S(COUNT);
  40.                                 my_head->head_p = sp;
  41.                                 my_head->last_p = sp;
  42.                                 cout<<"p:thread:"<<pthread_self()<<" prod "<<++my_head->len<<" data is:";
  43.                                 (my_head->last_p->data).prin();
  44.                                 cout<<"\n";
  45.                                 sp = NULL;
  46.                         }
  47.                         else
  48.                         {
  49.                                 QUE_S_P sp = new QUE_S(COUNT);
  50.                                 my_head->last_p->next = sp;
  51.                                 sp->priv = my_head->last_p;
  52.                                 my_head->last_p = sp;
  53.                                 cout<<"p:thread:"<<pthread_self()<<" prod "<<++my_head->len<<" data is:";
  54.                                 my_head->last_p->data.prin();
  55.                                 cout<<"\n";
  56.                                 sp = NULL;
  57.                         }
  58.                 }
  59.                 //cout<<"pro unlock:\n";
  60.                 ret = pthread_mutex_unlock(&my_head->pmut);
  61.                 debug_return(ret);
  62.                 //cout<<"pro signal:\n";
  63.                 ret = pthread_cond_signal(&my_head->pcon);
  64.                 debug_return(ret);

  65.         }

  66. }

  67. void* cus(void* arg)
  68. {
  69.         int ret = 0;
  70.         QUE_H* my_head;
  71.         my_head = (QUE_H*)arg;
  72.         QUE_S_P tmp=NULL;

  73.         while(1)//消費方式為一個消費執行緒只消費一個元素,來模擬多消費執行緒競爭
  74.         {
  75.                 ret = pthread_mutex_lock(&my_head->pmut);
  76.                 debug_return(ret);
  77.                 while(my_head->head_p == NULL) //如果已經消費完放棄鎖,等到條件及有元素供消費
  78.                 {
  79.                         //cout<<"cus cond wait\n";
  80.                         ret = pthread_cond_wait(&my_head->pcon,&my_head->pmut);
  81.                         debug_return(ret);
  82.                 }

  83.                 if(my_head->len == 1)//消費如果只剩下一個元素處理
  84.                 {
  85.                         cout<<"c:thread:"<<pthread_self()<<" cost "<<my_head->len--<<" data is:";
  86.                         my_head->last_p->data.prin();
  87.                         delete my_head->last_p;
  88.                         my_head->head_p = NULL;
  89.                         my_head->last_p = NULL;
  90.                         COUNT--;
  91.                 }
  92.                 else//否則處理如下
  93.                 {
  94.                         cout<<"c:thread:"<<pthread_self()<<" cost "<<my_head->len--<<" data is:";
  95.                         my_head->last_p->data.prin();
  96.                         cout<<"\n";
  97.                         tmp = my_head->last_p->priv;
  98.                         delete my_head->last_p;
  99.                         my_head->last_p= tmp;
  100.                         tmp=NULL;
  101.                         COUNT--;
  102.                 }

  103.                 ret = pthread_mutex_unlock(&my_head->pmut);
  104.                 debug_return(ret);
  105.         }

  106. }


  107. int main(void)
  108. {
  109.         QUE_H my_head;
  110.         int ret = 0;
  111.         pthread_t tid[MAX_TID];
  112.         int tid_num = 0;
  113.         int i = 0;

  114.         ret = pthread_mutex_init(&my_head.pmut,NULL);
  115.         debug_return(ret);
  116.         ret = pthread_cond_init(&my_head.pcon,NULL);
  117.         debug_return(ret);
  118.         //一個生產者
  119.         ret = pthread_create(tid+tid_num,NULL,pro,(void*)&my_head);
  120.         debug_return(ret);
  121.         tid_num++;
  122.         //n個消費者
  123.         ret = pthread_create(tid+tid_num,NULL,cus,(void*)&my_head);
  124.         debug_return(ret);
  125.         tid_num++;
  126.         ret = pthread_create(tid+tid_num,NULL,cus,(void*)&my_head);
  127.         debug_return(ret);
  128.         tid_num++;
  129.         ret = pthread_create(tid+tid_num,NULL,cus,(void*)&my_head);
  130.         debug_return(ret);
  131.         tid_num++;
  132.         ret = pthread_create(tid+tid_num,NULL,cus,(void*)&my_head);
  133.         debug_return(ret);
  134.         tid_num++;
  135.         ret = pthread_create(tid+tid_num,NULL,cus,(void*)&my_head);
  136.         debug_return(ret);

  137.     //堵塞回收
  138.         for(i = 0;i<=tid_num;i++)
  139.         {
  140.                 ret = pthread_join( *(tid+i) , NULL );
  141.                 debug_return(ret);
  142.         }

  143.         ret=pthread_mutex_destroy(&my_head.pmut);
  144.         debug_return(ret);
  145.         ret=pthread_cond_destroy(&my_head.pcon);
  146.         debug_return(ret);

  147. }

作者微信:

               

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2139638/,如需轉載,請註明出處,否則將追究法律責任。

相關文章