互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。
但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。
同步:是指在互斥的基礎上(大多數情況),通過其它機制實現訪問者對資源的有序訪問。
在大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況必定是互斥的。
少數情況是指可以允許多個訪問者同時訪問資源,如“第一類讀寫者模型”。生產者-消費者模型
1. 生產者進行生產將物品放入倉庫,同一時間只能有一個生產者將物品放入倉庫,如果倉庫滿,生產者等待。
2. 消費者從倉庫中取出物品,同一時間只能有一個消費者取出物品,如果倉庫空,消費者等待;
3. 生產者將物品放入倉庫時消費者不能同時取;
4. 消費者取物品時生產者不能放入物品;
總之,就是生產者羣體或消費者羣體內部是互斥的,兩個羣體之間是同步的。
情況一: 單個消費者和單個生產者
利用互斥鎖和條件變量來實現互斥和同步:
// file producer_consumer.c
# include <pthread.h>
# include <stdio.h>
# include <stdlib.h>
# define BUFFER_SIZE 16
# define OVER -1
struct console {
int buf[BUFFER_SIZE]; // 緩衝區數組 循環
pthread_mutex_t lock; // 互斥鎖
int readpos, writepos; // 讀寫位置
pthread_cond_t notempty; // 緩衝區非空信號
pthread_cond_t notfull; // 緩衝區非滿信號
};
struct console buffer;
// 初始化緩衝區數據結構
void init(struct console *buf)
{
pthread_mutex_init(&buf->lock, NULL);
buf->readpos = 0;
buf->writepos = 0;
pthread_cond_init(&buf->notempty, NULL);
pthread_cond_init(&buf->notfull, NULL);
}
// 清理緩衝區
void destroy(struct console *buf)
{
pthread_mutex_destroy(&buf->lock);
buf->readpos = 0;
buf->writepos = 0;
pthread_cond_destroy(&buf->notempty);
pthread_cond_destroy(&buf->notfull);
}
// 向緩衝區寫入一個數據
void put(struct console *buf, int data)
{
pthread_mutex_lock(&buf->lock);
while ((buf->writepos+1)%BUFFER_SIZE == buf->readpos) // 緩衝區已滿,等待緩衝區非滿/消費者消費
{
printf("wait for buffer not full!!\n");
pthread_cond_wait(&buf->notfull, &buf->lock);
}
// 寫入數據
buf->buf[buf->writepos] = data;
buf->writepos = (++buf->writepos)%BUFFER_SIZE;
pthread_cond_signal(&buf->notempty);
pthread_mutex_unlock(&buf->lock);
}
// 從緩衝區讀取一個數據
int get(struct console *buf)
{
int val;
pthread_mutex_lock(&buf->lock);
while (buf->readpos == buf->writepos) // 緩衝區已空,等待緩衝區非空/生產者生產
{
printf("wait for buffer to not empty!!\n");
pthread_cond_wait(&buf->notempty, &buf->lock);
}
// 讀取數據
val = buf->buf[buf->readpos];
buf->readpos = (++buf->readpos)%BUFFER_SIZE;
pthread_cond_signal(&buf->notfull);
pthread_mutex_unlock(&buf->lock);
return val;
}
// 生產者線程函數體
void *producer(void *arg)
{
int val;
for (val = 0; val < 100; val++)
{
printf("put ---> %d\n", val);
put(&buffer, val);
}
put(&buffer, OVER);
printf("producer stopped!!\n");
return NULL;
}
// 消費者線程函數體
void *consumer(void *arg)
{
int val;
while (1)
{
val = get(&buffer);
if (val == OVER)
break;
printf("get ---> %d\n", val);
}
printf("consumer stopped!!\n");
return NULL;
}
int main(void)
{
pthread_t th_a, th_b;
void *retval;
init(&buffer);
pthread_create(&th_a, NULL, producer, NULL); // 創建生產者線程
pthread_create(&th_b, NULL, consumer, NULL); // 創建消費者線程
pthread_join(th_a, &retval); // 等待生產者線程結束
pthread_join(th_b, &retval); // 等待消費者線程結束
destroy(&buffer);
return 0;
}
情況二: 多個消費者和多個生產者
利用互斥鎖和信號量來實現互斥和同步:
// file producer_consumer.c
# include <pthread.h>
# include <stdio.h>
# include <stdlib.h>
# define BUFFER_SIZE 16
# define OVER -1
#define PRODUCT_NUM 5
#define CONSUME_NUM 5
struct console {
int buf[BUFFER_SIZE]; // 緩衝區數組 循環
int readpos, writepos; // 讀寫位置
sem_t empty_sem; // 緩衝區非空信號
sem_t full_sem; // 緩衝區非滿信號
};
struct console buffer;
pthread_mutex_t producer_lock;
pthread_mutex_t consumer_lcok;
// 初始化緩衝區數據結構
void init(struct console *buf)
{
buf->readpos = 0;
buf->writepos = 0;
sem_init(&empty_sem, 0, BUFFER_SIZE);
sem_init(&full_sem, 0, 0);
pthread_mutex_init(&producer_lock, NULL);
pthread_mutex_init(&consumer_lock, NULL);
}
// 清理緩衝區
void destroy(struct console *buf)
{
buf->readpos = 0;
buf->writepos = 0;
sem_destroy(&buf->empty_sem);
sem_destroy(&buf->full_sem);
pthread_mutex_destroy(&producer_lock);
pthread_mutex_destroy(&consumer_lock);
}
// 向緩衝區寫入一個數據
void put(struct console *buf, int data)
{
sem_wait(&buf->empty_sem);
// 寫入數據
buf->buf[buf->writepos] = data;
buf->writepos = (++buf->writepos)%BUFFER_SIZE;
sem_post(&buf->full_sem);
}
// 從緩衝區讀取一個數據
int get(struct console *buf)
{
int val;
sem_wait(&buf->full_sem);
// 讀取數據
val = buf->buf[buf->readpos];
buf->readpos = (++buf->readpos)%BUFFER_SIZE;
sem_post(&buf->buf->empty_sem);
return val;
}
// 生產者線程函數體
void *producer(void *arg)
{
static int val = 0;
pthread_mutex_lock(&producer_lock);
val++;
if (val != 100)
{
printf("put ---> %d\n", val);
put(&buffer, val);
}
else
{
put(&buffer, OVER);
printf("producer stopped!!\n");
}
return NULL;
}
// 消費者線程函數體
void *consumer(void *arg)
{
int val;
pthread_mutex_lock(&consumer_lock);
{
val = get(&buffer);
printf("get ---> %d\n", val);
}
if (val == OVER)
printf("consumer stopped!!\n");
pthread_mutex_unlock(&consumer_lock);
return NULL;
}
int main(void)
{
pthread_t th_a[PRODUCT_NUM], th_b[CONSUME_NUM];
void *retval;
init(&buffer);
for(int i = 0; i != PRODUCT_NUM; ++i)
{
pthread_create(&th_a[i], NULL, producer, NULL); // 創建生產者線程
}
for(int i = 0; i != CONSUME_NUM; ++i)
{
pthread_create(&th_b[i], NULL, consumer, NULL); // 創建消費者線程}
//銷燬線程
for(int i = 0; i != PRODUCT_NUM; ++i)
{
pthread_join(th_a[i], &retval); // 等待生產者線程結束
}
for(int i = 0; i != PRODUCT_NUM; ++i){
pthread_join(th_b[i], &retval); // 等待消費者線程結束}
//銷燬緩衝區
destroy(&buffer);
return 0;
}