多執行緒用互斥鎖和條件變數實現生產者和消費者-------迴圈任務佇列
阿新 • • 發佈:2019-02-13
互斥鎖與條件變數簡介
在多執行緒的環境中,全域性變數會被各執行緒共享,因此在操作全域性變數的時候需要採用鎖機制,在linux裡最常用的鎖就是互斥鎖,互斥鎖使用方法如下
<pre name="code" class="cpp">//執行緒A
pthread_mutex_lock(&lock);
...; //對共享資料操作
pthread_mutex_unlock(&lock);
這段程式碼會包括在每一個試圖操作共享變數的執行緒裡面,這樣就能實現共享變數(臨界資源)的互斥使用。
這樣也會遇見一個問題,每個執行緒在臨界資源釋放之後都會爭奪鎖,獲得鎖的執行緒會繼續執行下去,其他執行緒都將阻塞在原地直到臨界資源被釋放後重新爭奪鎖,這樣很容易造成有的執行緒一直使用鎖而有的執行緒一直搶不到鎖,從而造成餓死的現象使程式執行效率十分低下。
為了解決這個問題linux提供了條件變數來使執行緒之間同步。
條件變數的使用方法如下
<pre name="code" class="cpp">//執行緒A pthread_mutex_lock(&lock); if(超前) pthread_cond_wait(&不超前,&lock); ...; //對共享資料操作 pthread_mutex_unlock(&lock); //執行緒B pthread_mutex_lock(&lock); if(A不超前) pthread_cond_signal(不超前); ...; //對共享資料操作 pthread_mutex_unlock(&lock);
因為條件變數的等待涉及到解鎖和加鎖操作,所以都將它放在鎖內執行。許多人對條件變數的使用理解的不是很正確,在執行wait操作的過程中先執行了解鎖操作,然後阻塞在該條件變數上面,直到某個執行緒傳送訊號來啟用阻塞的執行緒。注意:當被阻塞的執行緒被啟用之後它並沒有立即往下面執行,而是等待佔有鎖的執行緒釋放鎖,然後再和其他執行緒共同去爭搶鎖的使用權,獲得鎖之後才可以繼續往下執行。
有了以上的理解我們來寫一段程式碼。
這是一個經典的生產者和消費者的例子,執行緒producer產生資料寫在buf迴圈緩衝區,執行緒consumer將buf緩衝區的資料取出來並列印,迴圈緩衝區節省了空間,多執行緒提高了效率。這段程式碼可應用在許多場景之中。#include <stdio.h> #include <string.h> #include <stdlib.h> #include <pthread.h> #define BUFFER_SIZE 512 pthread_mutex_t lock; //互斥鎖 pthread_cond_t notempty; pthread_cond_t notfull; int buf[BUFFER_SIZE]; int writepos, readpos; int writenum = 0; int readnum = 0; int exit_condition = 0; //變數初始化 void init() { pthread_mutex_init(&lock,NULL); pthread_cond_init(¬empty,NULL); pthread_cond_init(¬full,NULL); writepos = 0; readpos = 0; } //寫操作 void put(int data) { //這個mutex_lock主要是用來保護wait等待臨界時期的情況 pthread_mutex_lock(&lock); while((writepos+1)%BUFFER_SIZE == readpos) { pthread_cond_wait(¬full,&lock); } writenum++; buf[writepos] = data; writepos++; if(writepos>=BUFFER_SIZE) writepos=0; //緩衝區滿,將回到原點 pthread_cond_signal(¬empty); pthread_mutex_unlock(&lock); } //讀操作 int get() { int data; pthread_mutex_lock(&lock); while(writepos == readpos) { pthread_cond_wait(¬empty,&lock); } readnum++; data = buf[readpos];//從緩衝區讀資料 readpos++; if(readpos>=BUFFER_SIZE) readpos=0; pthread_cond_signal(¬full); pthread_mutex_unlock(&lock); return data; } void* producer(void *data) { int i, j; for(i = 0; i < 1000; i++){ for(j = i; j < 1000; j++) put(j); } exit_condition = 1; return NULL; } void *consumer(void *data) { int local_data; while(1) { local_data = get(); if(exit_condition == 1 && readnum == writenum) break; printf("%d ", local_data); } return NULL; } int main(int argc, char *argv[]) { pthread_t produce_id, consumer_id; init(); pthread_create(&produce_id, NULL, producer, NULL); pthread_create(&consumer_id, NULL, consumer, NULL); pthread_join(produce_id,NULL); pthread_join(consumer_id, NULL); return 0; }
希望以上的東西對您有用。