信号量相当于红灯,是在多线程多进程下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其他想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量,需要将sem_wait()放在每个关键代码的首端,做减减操作,将sem_post()放在段尾,做加加操作。
举个生活中的例子:
假设停车场只有三个车位,一开始三个车位都是空的。此时如果同时来了五辆车,看门人允许其中的三辆的直接进入,然后放下车栏,剩下的车必须在入口处等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后打开车栏,放入外面的一辆车进去,如果又离开两辆车,则又可以放入两辆车进去,如此往复。这个停车系统中,车位是公共资源,每辆车就是每个线程,看门人起的就是信号量的作用。
下面是信号量相关的API
信号量的类型 sem_t
int sem_init(sem_t *sem,int pshared,unisgned int value);
-初始化信号量
-参数:
-sem:信号量变量的地址
-pshared:0 用在线程间,非0 用在进程间
value:信号量中的值
int sem_destroy(sem_t *sem);
-释放资源
int sem_wait(sem_t *sem);
-对信号量加锁,调用一次对信号量的值-1,如果值为0,就阻塞
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);
-对信号量解锁,调用一次对信号量的值+1
int sem_getvalue(sem_t *sem,int *sval);
以生产者消费者模型为例,用信号量实现这个功能
#include <semaphare.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex;
sem_t psem;
sem_t csem;
struct Node{//创建链表
int num;
struct Node *next;
};
struct Node *head=NULL;//设置头结点为空
void *producer(void *arg)//生产者
{
while(1)
{
sem_wait(&psem);//每进去一个车,公共车位就少一个,所以减一
pthread_mutex_lock(&mutex);//上锁
struct Node* newNode=(struct Node*)malloc(sizeof(struct Node));//开辟一块空间
newNode->next=head;
head=newNode;
newNode->num=rand()%100;//取随机数
printf("add node,num:%d,tid:%ld\n",newNode->num,pthread_self());
pthread_mutex_unlock(&mutex);//解锁
sem_post(&csem);//每次出去一个车,公共车位就多一个,所以加一
usleep(100);
}
return NULL;
}
void *customer(void *arg)
{
while(1)
{
sem_wait(&csem)//每次进去一个车,车位就减一
pthread_mutex_lock(&mutex);//加锁
struct Node *tmp=head;
if(head!=NULL)
{
head=head->next;
printf("del node,num:%d,tid:%ld\n",tmp->num,pthread_self());
free(tmp);
pthread_mutex_unlock(&mutex);//解锁
sem_post(&psem);//每次出去一个车,车位就增加一个
}
return NULL;
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);//初始化互斥锁
sem_init(&psem,0,8);//初始化8个生产者空间
sem_init(&csem,0,0);//初始化消费者,刚开始还没有消费,所以初始化为0
pthread_t ptids[5],ctids[5];
for(int i=0;i<5;i++)
{
pthread_create(&ptids[i],NULL,producer,NULL);//创建五个生产者线程
pthread_create(&ctids[i],NULL,customer,NULL);//创建五个消费者线程
}
for(int i=0;i<5;i++)
{
pthread_join(ptids[i],NULL);//回收生产者子线程
pthread_join(ctids[i],NULL);//回收消费者子线程
}
while(1)
{
sleep(10);
}
pthread_mutex_destroy(&mutex);//解除互斥锁
sem_destroy(&psem);//释放生产者资源
sem_destroy(&csem);//释放消费者资源
pthread_exit(NULL);
return 0;
}
所以输出结果为:
通过观察我们会看到,有时候增加一个数字,就相应的减少一个数字,有时候同时增加几个数字,则按照栈操作顺序输出数字。
所以,运用信号量可以非常明显的实现了这个功能。