信号量的基本思想是通过一个计数器和一组相关的操作来实现资源的控制和同步。计数器表示可用的资源数量,每次访问共享资源时,线程需要先申请信号量,如果信号量的计数器大于零,则线程可以访问共享资源并将计数器减一;如果计数器为零,则线程需要等待,直到其他线程释放资源,使计数器变为非零。
信号量和线程锁Mutex有很多相似之处,也有些不同点:
①Mutex只能是0和1,0表示上锁,1表示释放锁。但是semaphore可以大于1。
②信号量可以用于线程间,也可以用于进程间通信。但是Mutex只能用于线程间通信。
主要有以下几个函数:
int sem_init (sem_t *sem, int pshared, unsigned int value);//初始化信号量
int sem_wait(sem_t *sem); //阻塞等,等不到就一直阻塞着等
int sem_trywait(sem_t *sem); //非阻塞等
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); //定时等
生产者消费者模型如下:
创建个仓库,里面有5个空位,可以放0-5件商品。
如果放满了没人来取商品,就是爆仓;
如果取货时没货,就是崩盘。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#define _POSIX_C_SOURCE 200809L
#include <semaphore.h>#define NUM 5int q[NUM];//仓库,最多能放5件物品sem_t* blank_number;
sem_t* goods_number;void* producer(void* arg)
{int i = 0;while(1){sem_wait(blank_number);//抢空位,抢不到就一直等q[i] = rand() % 100 + 1;printf("produce %d\n", q[i]);sem_post(goods_number); //增加一个商品i = (i + 1) % NUM;sleep(rand()%3); //每次休息1-3秒钟}
}void* consumer(void *arg)
{int i = 0;while(1){sem_wait(goods_number);//抢商品,抢不到就阻塞printf("consume %d\n", q[i]);q[i] = 0; //消费掉这个商品sem_post(blank_number);//释放一个空位i = (i + 1) % NUM;sleep(rand() % 3); //每次休息1-3秒钟}
}int main()
{
//使用time(NULL)函数返回的当前时间作为种子,然后传递给srand函数,以初始化伪随机数生成器。
//通常情况下,如果不给伪随机数生成器提供一个种子,那么每次程序运行时生成的随机数都是相同的。srand(time(NULL));//下面2行是Linux的写法// sem_init(blank_number, 0, NUM); //空位,0表示单进程多线程,一开始5个空位// sem_init(blank_number, 0, 0); //产品,0表示单进程多线程,一开始0个商品//下面2行是mac的写法blank_number = sem_open("blank_number", O_CREAT, S_IRUSR | S_IWUSR, NUM);goods_number = sem_open("goods_number", O_CREAT, S_IRUSR | S_IWUSR, 0);pthread_t pid, cid;//生产者ID和消费者IDpthread_create(&pid, NULL, producer, NULL); //起生产者线程pthread_create(&cid, NULL, consumer, NULL); //起消费者线程pthread_join(pid, NULL);pthread_join(cid, NULL);return 0;
}
结果是:
zhanghaodeMacBook-Pro:cpp_excise zhanghao$ ./a.out
produce 96
produce 85
consume 96
consume 43
consume 18
produce 4
produce 56
produce 64
produce 81
^C
注:这个代码,可以检测到崩盘:消费如果为0,说明当前没货,就崩盘了。但爆仓检测不到。