互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。
但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。
在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。
少数情况是指可以允许多个访问者同时访问资源,如“第一类读写者模型”。生产者-消费者模型
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;
}