1. 程式人生 > >linux中的生產者和消費者問題--訊號量 互斥 同步

linux中的生產者和消費者問題--訊號量 互斥 同步

互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。

同步:是指在互斥的基礎上(大多數情況),通過其它機制實現訪問者對資源的有序訪問。在大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個訪問者同時訪問資源,如“第一類讀寫者模型”。

生產者-消費者模型

       生產者-消費者模型是指:

1.  生產者進行生產將物品放入倉庫,同一時間只能有一個生產者將物品放入倉庫,如果倉庫滿,生產者等待。

2.  消費者從倉庫中取出物品,同一時間只能有一個消費者取出物品,如果倉庫空,消費者等待;

3.  生產者將物品放入倉庫時消費者不能同時取;

4.  消費者取物品時生產者不能放入物品;

總之,就是生產者群體或消費者群體內部是互斥的,兩個群體之間是同步的。

當只有一個生產者、消費者時,由於同一群體內部不需要互斥,所以只需在群體之間實 現同步即可。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

#define N 2   // 消費者或者生產者的數目
#define M 10 // 緩衝數目

int in = 0;   // 生產者放置產品的位置
int out = 0; // 消費者取產品的位置

int buff[M] = {0}; // 緩衝初始化為0, 開始時沒有產品

sem_t empty_sem; // 同步訊號量, 當滿了時阻止生產者放產品
sem_t full_sem;   // 同步訊號量, 當沒產品時阻止消費者消費
pthread_mutex_t mutex; // 互斥訊號量, 一次只有一個執行緒訪問緩衝

int product_id = 0;   //生產者id
int prochase_id = 0; //消費者id

/* 列印緩衝情況 */
void print()
{
int i;
for(i = 0; i < M; i++)
   printf("%d ", buff[i]);
printf("/n");
}

/* 生產者方法 */
void *product()
{
int id = ++product_id;

while(1)
{
   // 用sleep的數量可以調節生產和消費的速度,便於觀察
   sleep(1);
   //sleep(1);
  
   sem_wait(&empty_sem);
   pthread_mutex_lock(&mutex);
  
   in = in % M;
   printf("product%d in %d. like: /t", id, in);
  
   buff[in] = 1;  
   print();  
   ++in;
  
   pthread_mutex_unlock(&mutex);
   sem_post(&full_sem);  
}
}

/* 消費者方法 */
void *prochase()
{
int id = ++prochase_id;
while(1)
{
   // 用sleep的數量可以調節生產和消費的速度,便於觀察
   sleep(1);
//sleep(1);
  
   sem_wait(&full_sem);
   pthread_mutex_lock(&mutex);
  
   out = out % M;
   printf("prochase%d in %d. like: /t", id, out);
  
   buff[out] = 0;
   print();
   ++out;
  
   pthread_mutex_unlock(&mutex);
   sem_post(&empty_sem);
}
}

int main()
{
pthread_t id1[N];
pthread_t id2[N];
int i;
int ret[N];

// 初始化同步訊號量
int ini1 = sem_init(&empty_sem, 0, M);
int ini2 = sem_init(&full_sem, 0, 0);  
if(ini1 && ini2 != 0)
{
   printf("sem init failed /n");
   exit(1);
}
//初始化互斥訊號量
int ini3 = pthread_mutex_init(&mutex, NULL);
if(ini3 != 0)
{
   printf("mutex init failed /n");
   exit(1);
}
// 建立N個生產者執行緒
for(i = 0; i < N; i++)
{
   ret[i] = pthread_create(&id1[i], NULL, product, (void *)(&i));
   if(ret[i] != 0)
   {
    printf("product%d creation failed /n", i);
    exit(1);
   }
}
//建立N個消費者執行緒
for(i = 0; i < N; i++)
{
   ret[i] = pthread_create(&id2[i], NULL, prochase, NULL);
   if(ret[i] != 0)
   {
    printf("prochase%d creation failed /n", i);
    exit(1);
   }
}
//銷燬執行緒
for(i = 0; i < N; i++)
{
   pthread_join(id1[i],NULL);
   pthread_join(id2[i],NULL);
}
exit(0);
}

這個程式容易出錯誤的就是設定同步訊號量和互斥訊號量的順序出錯, 如在生產者或者消費者方法中把互斥訊號量放在同步訊號量的外層. 這樣在內層中某個sem_wait不一定能通過, 從而造成死鎖現象.

在多個消費者和一個消費者的不同就是是否需要互斥鎖。