1. 程式人生 > >多執行緒用互斥鎖和條件變數實現生產者和消費者-------迴圈任務佇列

多執行緒用互斥鎖和條件變數實現生產者和消費者-------迴圈任務佇列

互斥鎖與條件變數簡介

在多執行緒的環境中,全域性變數會被各執行緒共享,因此在操作全域性變數的時候需要採用鎖機制,在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操作的過程中先執行了解鎖操作,然後阻塞在該條件變數上面,直到某個執行緒傳送訊號來啟用阻塞的執行緒。注意:當被阻塞的執行緒被啟用之後它並沒有立即往下面執行,而是等待佔有鎖的執行緒釋放鎖,然後再和其他執行緒共同去爭搶鎖的使用權,獲得鎖之後才可以繼續往下執行。

有了以上的理解我們來寫一段程式碼。

#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;
}
這是一個經典的生產者和消費者的例子,執行緒producer產生資料寫在buf迴圈緩衝區,執行緒consumer將buf緩衝區的資料取出來並列印,迴圈緩衝區節省了空間,多執行緒提高了效率。這段程式碼可應用在許多場景之中。

希望以上的東西對您有用。