生产者消费者模型-信号量和条件变量
生产者消费者模型
生产者消费者模型是大多数框架中用到的一种同步线程,可以让不同线程按照开发者思路访问共享资源的模型。
生产者消费者模式的思路就是通过一个容器来解决生产者和消费者强耦合的问题。生产者和消费者不直接对接,而是通过这个容器(阻塞队列)让各个生产者(线程)消费者(线程)按顺序获取容器里的资源和工作,并且生产者不用等待消费者处理完成后再生产资源。
图解如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-THJQZxVU-1588901083766)(C:\Users\gl\AppData\Roaming\Typora\typora-user-images\1583046433451.png)]
生产者将生产的共享资源放入到这个带阻塞的容器,消费者从这个容器里取出共享资源消费。
模型实现-信号量和条件变量
C/C++实现这个模型,可以使用信号量和条件变量与锁的配合使用
1.互斥锁+条件变量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
// 链表节点的结构体
struct Node
{
int data;
struct Node* next;
};
// 创建第一个节点就是数据节点的链表
// 定义一个指针, 永远指向头结点
struct Node* head = NULL;
// 创建条件变量
pthread_cond_t cond;
// 创建互斥锁变量
pthread_mutex_t mutex;
// 生产者回调
void* product(void* arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
// 1. 创建新节点
struct Node* pNew = (struct Node*)malloc(sizeof(struct Node));
// 2. 新节点初始化
pNew->data = rand() % 1000;
// 3. 新节点挂到链表的头部
pNew->next = head;
head = pNew;
printf("++++++ tid: %ld, number: %d\n", pthread_self(), pNew->data);
pthread_mutex_unlock(&mutex);
// 4. 通信阻塞的消费者线程解除阻塞
//pthread_cond_signal(&cond);//唤醒至少一个阻塞线程
pthread_cond_broadcast(&cond);//唤醒全部阻塞线程
sleep(rand()%3);
}
return NULL;
}
// 消费者回调
void* customer(void* arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
// 1. 从头部取出一个节点, 打印信息
while(head == NULL)
{
// 链表中没有节点了
// 阻塞消费者线程
***// 1. 如果pthread_cond_wait阻塞了线程, 会将线程加锁成功的互斥锁解锁
// 2. 解锁成功之后, 阻塞线程
// 3. 当线程从该函数上解除阻塞的时候, 这个线程会再次对互斥锁加锁
pthread_cond_wait(&cond, &mutex);
}
struct Node* pcur = head;
printf("----- tid: %ld, number: %d\n", pthread_self(), pcur->data);
// 2. head指针后移
head = head->next;
// 3. 将这个节点从链表中删除
free(pcur);
pthread_mutex_unlock(&mutex);
sleep(rand()%3);
}
}
int main()
{
// 初始化条件变量
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
// 创建生产者线程
pthread_t tid[5];
// 创建消费者线程
pthread_t ctid[5];
for(int i=0; i<5; ++i)
{
pthread_create(&tid[i], NULL, product, NULL);
pthread_create(&ctid[i], NULL, customer, NULL);
}
for(int i=0; i<5; ++i)
{
pthread_join(tid[i], NULL);
pthread_join(ctid[i], NULL);
}
// 销毁条件变量
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
2.互斥锁+信号量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
// 链表节点的结构体
struct Node
{
int data;
struct Node* next;
};
// 创建第一个节点就是数据节点的链表
// 定义一个指针, 永远指向头结点
struct Node* head = NULL;
// 创建互斥锁变量
pthread_mutex_t mutex;
// 信号量
sem_t psem;
sem_t csem;
// 生产者回调
void* product(void* arg)
{
while(1)
{
// 消费者消耗一个资源
sem_wait(&psem);
pthread_mutex_lock(&mutex);
// 1. 创建新节点
struct Node* pNew = (struct Node*)malloc(sizeof(struct Node));
// 2. 新节点初始化
pNew->data = rand() % 1000;
// 3. 新节点挂到链表的头部
pNew->next = head;
head = pNew;
printf("++++++ tid: %ld, number: %d\n", pthread_self(), pNew->data);
pthread_mutex_unlock(&mutex);
//通知消费者消费
sem_post(&csem);
sleep(rand()%3);
}
return NULL;
}
// 消费者回调
void* customer(void* arg)
{
while(1)
{
// 消费者消费, 消耗一个资源
sem_wait(&csem);
pthread_mutex_lock(&mutex);
struct Node* pcur = head;
printf("----- tid: %ld, number: %d\n", pthread_self(), pcur->data);
// 2. head指针后移
head = head->next;
// 3. 将这个节点从链表中删除
free(pcur);
pthread_mutex_unlock(&mutex);
// 通知生产者生产
sem_post(&psem);
sleep(rand()%3);
}
}
int main()
{
// 初始化生产者信号量
sem_init(&psem, 0, 5);
// 初始化消费者信号量
sem_init(&csem, 0, 0);
pthread_mutex_init(&mutex, NULL);
// 创建生产者线程
pthread_t tid[5];
// 创建消费者线程
pthread_t ctid[5];
for(int i=0; i<5; ++i)
{
pthread_create(&tid[i], NULL, product, NULL);
pthread_create(&ctid[i], NULL, customer, NULL);
}
for(int i=0; i<5; ++i)
{
pthread_join(tid[i], NULL);
pthread_join(ctid[i], NULL);
}
// 销毁信号量
sem_destroy(&psem);
sem_destroy(&csem);
pthread_mutex_destroy(&mutex);
return 0;
}
; i<5; ++i)
{
pthread_join(tid[i], NULL);
pthread_join(ctid[i], NULL);
}
// 销毁信号量
sem_destroy(&psem);
sem_destroy(&csem);
pthread_mutex_destroy(&mutex);
return 0;
}
参考文章:https://blog.csdn.net/lvxin15353715790/article/details/89143121