参考:http://blog.youkuaiyun.com/pcliuguangtao/article/details/6744557
1. 信号量基本术语
现代计算机系统中,多个进程可以并发执行,进程间必然存在共享资源和相互合作的问题。
同步主要是用户多个进程相互协作,共同完成任务(例如进程执行存在先后顺序),是进程间的直接制约问题;
互斥则主要是为了多个进程分时使用有限的资源。
信号量(semaphore)是1965年由荷兰人Dijkstra提出的一种卓有成效的进程间同步及互斥工具。信号量在操作系统中实现时一般作为一个整数变量,这种信号量称为整型信号量。信号量S的物理意义:
S >= 0表示某资源的可用数;
S<0 其绝对值表示阻塞队列中等待该资源的进程数目;
信号量的两个操作方法是PV,P操作为S=S-1;表示申请一个资源,V操作为S=S+1;表示释放一个资源。因为对整数的加减1操作是在一个指令完成的,而硬件能够保证中断只能发生在指令之间,所以PV操作不会被中断打扰执行结果。
P
{
S = S-1;
if(s<0)
Wait(S); --------当前进程进入等待队列等待
}
V
{
S=S+1;
if(S<=0)
Resume(S); ----------唤醒等待队列中的一个进程
}
2.利用信号量实现互斥
初始化信号量mutex = 1; 当进程进入临界区时执行P操作,退出临界区时执行V操作。
P(mutex)
临界区代码;(操作互斥资源)
V(mutex)
3. 利用信号量实现同步
此时可以把信号想象成代表一个消息。当S=0表示表示消息未产生;当S>0则表示消息已经产生。例如:
(1)单缓冲区的生产者和消费者问题。
生产者进程P1不断地生产产品送入缓冲区,消费者进程P2不断地从缓冲区中提取产品消费。为了实现P1与P2进程间的同步,需要设置一个信号量S1,并且初始化为1,表示缓冲区为空,可以将产品放入缓冲区中;还需要设置另一个另一个信号量S2,初始值为0,表示缓冲区没有产品,可以提取消费。
P1:生产一个产品--->P(S1)测试缓冲区是否为空----->产品送缓冲区---->V(S2)设置有产品---->重复
P2: P(S2)测试是否有产品----->从缓冲区取出产品------->V(S1)设置缓冲区为空------->消费--->重复
(2)多缓冲区同步问题
设有一个生产者和一个消费者,缓冲区可以存放n件物品,生产者不断地生产产品,消费者不断地消费产品。
设置3个信号量,S, S1,S2。其中S是一个互斥信号量初值为1,对缓冲区资源进行互斥控制,S1表示是否可以将物品放入缓冲区,初值为n,S2表示缓冲区中是否有物品,初值为0。同步过程如下:
P1:生产一个产品----->P(S1)--->P(S)--->产品送缓冲区--->V(S)---->V(S2)
P2:P(S2)------>P(S)--->从缓冲区取出一个产品----->V(S)----->V(S1)----->消费
1、先生产再消费,同步执行
/*
*简单的生产者 消费者模型
*/
#include <stdio.h>
#include <string.h>
#include <semaphore.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
/*封装 P/V 操作*/
void P(sem_t* sem)
{
if(sem_wait(sem))
perror("P operating error");
}
void V(sem_t* sem)
{
if(sem_post(sem))
perror("V operating error");
}
/*定义共享缓冲区*/
static char share_buf[50];
/*定义两个信号量以及其初始化函数*/
sem_t empty_sem;
sem_t full_sem;
void init_sem()
{
sem_init(&empty_sem,0,1);
sem_init(&full_sem,0,0);
}
/*生产者*/
void* produce(void* arg)
{
char buf[50]={0};
while(1){
printf("Input message>>\n");
fgets(buf,sizeof(buf),stdin);
printf("Produce item is>>%s",buf);
/*将消息放入缓冲区*/
P(&empty_sem);
memcpy(share_buf,buf,sizeof(buf));
V(&full_sem);
}
return NULL;
}
/*消费者*/
void* consumer(void* arg)
{
char buf[50]={0};
while(1){
P(&full_sem);
memcpy(buf,share_buf,sizeof(share_buf));
V(&empty_sem);
/*显示获得信息*/
printf("Consume item is<<%s",buf);
}
return NULL;
}
int main()
{
pthread_t produce_tid;
pthread_t consumer_tid;
init_sem();
pthread_create(&produce_tid,NULL,produce,NULL);
pthread_create(&consumer_tid,NULL,consumer,NULL);
pthread_join(produce_tid,NULL);
pthread_join(consumer_tid,NULL);
return 0;
}
2、两个线程同步即顺序执行让count++操作
/*
*线程同步之全局变量
*/
#include <stdio.h>
#include <string.h>
#include <semaphore.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
/*定义共享变量*/
int count = 0;
/*定义两个信号量以及其初始化函数*/
sem_t empty_sem;
sem_t full_sem;
void init_sem()
{
sem_init(&empty_sem,0,1);
sem_init(&full_sem,0,0);
}
/*封装 P/V 操作*/
void P(sem_t* sem)
{
if(sem_wait(sem))
perror("P operating error");
}
void V(sem_t* sem)
{
if(sem_post(sem))
perror("V operating error");
}
/*最先得到执行
因为初始化时候empty_sem = 1
P操作即尝试让empty_sem-1>=0因此不会阻塞,往下执行;
执行完之后执行V操作即让full_sem+1=1;
这样pthSecondFun得到执行了;
*/
void* pthFirstFun(void* arg)
{
char buf[50]={0};
while(1){
P(&empty_sem);
count++;
printf("pthFirstFun count=%d\n", count);
V(&full_sem);
}
return NULL;
}
/*后得到执行
因为初始化时候full_sem = 0
P操作即尝试让full_sem-1<0因此阻塞;
当pthFirstFun执行V操作之后让full_sem+1=1,
这里再次执行P操作即full_sem-1>=0,因此不再阻塞了,继续往下执行,
执行完之后执行V操作即让empty_sem+1=1,这样pthFirstFun再次得到执行
*/
void* pthSecondFun(void* arg)
{
char buf[50]={0};
while(1){
P(&full_sem);
count++;
printf("pthSecondFun count=%d\n", count);
V(&empty_sem);
}
return NULL;
}
int main()
{
pthread_t pthFirst_tid;
pthread_t pthSecond_tid;
init_sem();
pthread_create(&pthFirst_tid,NULL,pthFirstFun,NULL);
pthread_create(&pthSecond_tid,NULL,pthSecondFun,NULL);
pthread_join(pthFirst_tid,NULL);
pthread_join(pthSecond_tid,NULL);
return 0;
}