前言
信号量广泛用于进程或线程间的同步和互斥,本质上是一个非负整数计数器。当信号量值大于 0 时,则可以访问公共资源,否则将阻塞访问
PV 是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1
信号量一般分为两种:
1:无名信号量,一般用于线程间和父子进程间同步或互斥,保存在内存中
2:有名信号量,一般用于普通进程间同步或互斥,创建一个文件保存在文件中
本文将以无名信号量来实现生产者消费者模型
一、无名信号量常用函数
1. int sem_init(sem_t *sem, int pshared, unsigned int value);
// 创建信号量
// pshared:控制信号量的类型,0 表示线程间共享,其它表示进程间共享
// value:信号量的初始值
3. int sem_post(sem_t *sem);
// 以原子操作的方式给信号量的值加1(V操作),并唤醒调用 sem_wait 处于等待信号量状态的线程
4. int sem_wait(sem_t *sem);
// 以原子操作的方式给信号量的值减1(P操作),如果信号量值为 0 则等待,直到有线程调用 sem_post 增加了该信号量的值
5. int sem_trywait(sem_t *sem);
// 非阻塞方式,如果信号量的值为 0 则立即返回,返回错误 EAGAIN
6. int sem_getvalue(sem_t *sem, int *sval);
// 获取信号量的值,保存在 sval 中
7. int sem_destory(sem_t *sem);
// 销毁信号量,释放调用 sem_init 申请的资源
二、生产者-消费者模型代码
有一个长度为 N 的缓冲池为生产者和消费者所共有。只要缓冲池未满,生产者便可将消息送入缓冲池;只要缓冲池未空,消费者便可从缓冲池中取走一个消息。生产者往缓冲池放信息的时候,消费者不可操作缓冲池,反之亦然
代码实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define BUFF_SIZE 5 // 缓冲池大小
char count = 0; // 缓冲池中消息的数目
pthread_mutex_t mutex; // 互斥量,用于生产者和消费者互斥操作缓冲池
sem_t semCanCustom; // 消费者信号量,不为 0 时表示消费者可以消费资源
sem_t semCanProduct; // 生产者信号量,不为 0 时表示生产者可以生产资源
void* thread_producer(void* arg)
{
while (1)
{
sem_wait(&semCanProduct); // 等待缓冲池不满
pthread_mutex_lock(&mutex);
count++;
printf("count %d producer\n", count);
pthread_mutex_unlock(&mutex);
sem_post(&semCanCustom); // 告知消费者线程可以消费了
sleep(0.5);
}
}
void* thread_consumer(void* arg)
{
while (1)
{
sem_wait(&semCanCustom); // 等待缓冲池不空
pthread_mutex_lock(&mutex);
count--;
printf("count %d consumer\n", count);
pthread_mutex_unlock(&mutex);
sem_post(&semCanProduct); // 告知生产者线程可以生产了
sleep(0.5);
}
}
int main()
{
pthread_t ptid, ctid;
pthread_mutex_init(&mutex, NULL);
sem_init(&semCanCustom, 0, 0); // 消费者信号量初始值为 0,表示初始不能消费
sem_init(&semCanProduct, 0, BUFF_SIZE); // 生产者信号量初始值为缓冲池大小,表示初始可以生产 N 个资源
pthread_create(&ptid, NULL, thread_producer, NULL);
pthread_create(&ctid, NULL, thread_consumer, NULL);
pthread_join(ptid, NULL);
pthread_join(ctid, NULL);
pthread_mutex_destroy(&mutex);
sem_destroy(&semCanCustom);
sem_destroy(&semCanProduct);
return 0;
}