【信号量定义】
信号量从本质上是一个非负整数计数器,通过PV原子操作来达到控制公共资源的访问的目的。PV原子操作是对整数计数器信号量sem的操作。一次P操作使sem减1,而一次V操作使sem加1。当信号量sem的值大于等于0时,该进程(或线程)具有公共资源的访问权限;相反,当信号量sem的值小于0时,该进程(或线程)就将阻塞直到信号量sem的值大于等于0为止。
PV原子操作主要用于进程或线程间的同步和互斥这两种典型情况。若用于互斥,几个进程(或线程)往往只设置一个信号量sem;当信号量用于同步时,往往设置多个信号量,并安排不同的初始值来实现它们之间的顺序执行。
【相关函数】
(1)sem_init()用于创建一个信号量,并初始化它的值
头文件:#include <semaphore.h>
函数原型:int sem_init(sem_t *sem, int pthread, unsigned int value)
函数传入值:sem:信号量指针
pthread:决定信号量能否在几个进程间共享。由于目前Linux还没有实现进程间共享信号量,所以这个值只能是0,表示这个信号量是当前进程的局部信号量。(进程间信号量共享可通过semget()等函数实现)
value:信号量初始化值
函数返回值:成功返回0,失败返回-1
(2)P操作:sem_wait()对信号量的值减1,如果当前信号量为0,则函数堵塞,知道信号量大于等于1,才返回并将信号量的值减1
sem_trywait()也相当于P操作,但其不会阻塞而是立即返回。
(3)V操作:sem_post()对信号量的值加1,同时发出信号来唤醒等待的进程
(4)sem_getvalue()用于得到信号量的值
(5)sem_destroy()用于删除信号量
函数(2)—(5):
头文件:#include <pthread.h>
函数传入值:sem_t *sem:信号量指针
函数返回值:成功返回0,失败返回-1
【代码示例】
利用信号量实现生产消费,最多生产5个,最多消费5个
/****************************
*信号量,最多生产5个,最多消费5个
****************************/
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
typedef struct nList
{
int sum;
struct nList *next;
}product;
product *head = NULL;
product *tail = NULL;
product *new = NULL;
sem_t sem1;//5-0
sem_t sem2;//0-5
sem_t sem3;//1-0
void *fun1()
{
int p;
while (1)
{
sem_wait(&sem1);//减1
sem_wait(&sem3);//减1
//生产
p = add_list();
printf("生产[%d]n", p);
sem_post(&sem2);//加1
sem_post(&sem3);//加1
sleep(1);
}
}
void *fun2()
{
int p;
while (1)
{
sem_wait(&sem2);//减1
sem_wait(&sem3);//减1
//消费
p = del_list();
if (p < 0)
{
printf("无产品可消费!n");
}
else
{
printf("消费[%d]n", p);
}
sem_post(&sem1);//加1
sem_post(&sem3);//加1
}
}
/*****************************
*函数功能:向链表添加结点
*输入参数:(product *)head:链表头结点
*输出参数:(int)
(1)-1:添加失败
(2)>0:添加成功,返回节点数据
*****************************/
int add_list()
{
int n;
product *p = head;
srand((unsigned)time(NULL));
new = (product *)malloc(sizeof(product));
if (new == NULL)
{
perror("malloc");
return -1;
}
new->sum = rand()0+1;
n = new->sum;
if (head == NULL)
{
head = new;
}
else
{
tail->next = new;
}
tail = new;
tail->next = NULL;
return n;
}
/*****************************
*函数功能:从链表删除结点
*输入参数:(product *)head:链表头结点
*输出参数:(int)
(1)-1:删除失败
(2)>0:删除成功,返回节点数据
*****************************/
int del_list()
{
int n;
product *p = head;
if (head == NULL)
{
return -1;
}
else
{
head = head->next;
n = p->sum;
free(p);
p = NULL;
return n;
}
}
int main()
{
pthread_t tid1, tid2;
int ret;
//信号量初始化
ret = sem_init(&sem1, 0, 5);
if (ret < 0)
{
perror("sem_init");
exit(1);
}
ret = sem_init(&sem2, 0, 0);
if (ret < 0)
{
perror("sem_init");
exit(1);
}
ret = sem_init(&sem3, 0, 1);
if (ret < 0)
{
perror("sem_init");
exit(1);
}
ret = pthread_create(&tid1, NULL, fun1, NULL);
if (ret < 0)
{
perror("pthread_create");
exit(1);
}
pthread_create(&tid2, NULL, fun2, NULL);
if (ret < 0)
{
perror("pthread_create");
exit(1);
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
sem_destroy(&sem1);
sem_destroy(&sem2);
}