一、信号量是什么?
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用,它的功能类似于互斥锁,但是它能提供更为高级的方法,以便进程能够同步活动。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。
二、使用步骤
1.用下面一组函数(系统调用)来实现对临界资源的访问
代码如下(示例):
int sem_init(sem_t *sem,int pshared,unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem);
看一个例子,比如有两个线程都要往打印机上打东西,但是同一时刻只能打一个。
那么首先用sem_init
初始化一个信号量,注意pshared表示允许几个进程共享该信号量,一般设0用于进程内的多线程共享,要看是否支持进程共享,请查看下你的系统的man手册。
第三个参数value表示可用的资源的数目,即信号灯的数目,咱们这儿只有1个打印机所以设成1。
然后线程调用sem_wait
取获取这个信号灯,第一个线程一看,有1个,他就拿到了,然后可以继续后继操作,此时信号灯自动减1,变成0个。那么第二个线程调用sem_wait时就会阻塞在这儿了。
第一个线程完成打印后,调用sem_post
释放信号灯,信号灯数目变成1,将会唤醒等待的第二个线程,然后第二个线程接着打印。最后当所有任务完成后,主线程调用sem_destroy
释放这个信号量。
sem_wait
是一个函数,也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,将信号量的值将减到1。
如果对一个值为0的信号量调用sem_wait(),这个函数就会原地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加 一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。sem_trywait(sem_t *sem)
是函数sem_wait的非阻塞版,它直接将信号量sem减1,同时返回错误代码。
2.信号量实现顺序操作
代码如下:
/*************************************************************************
# FileName : pthreadsem.c
# Author : fengjunhui
# Email : 18883765905@163.com
************************************************************************/
#include<stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define N 64
typedef struct message{
char buf[N];
int len;
}msg_t;
sem_t sem_reverse;
sem_t sem_printf;
void* reverse_msgbuf(void* arg)
{
msg_t *msg = (msg_t *)arg;
int i = 0;
char tmp;
while(1){
sem_wait(&sem_reverse); //sem_reverse有资源,先执行
printf("reverse_msgbuf -------------\n");
#if 1
for(i = 0; i < msg->len/2; i ++){
tmp = msg->buf[i];
msg->buf[i] = msg->buf[msg->len - i - 1];
msg->buf[msg->len - i -1] = tmp;
}
#endif
sleep(1);
printf("reverse_msgbuf :%s\n",msg->buf);
sem_post(&sem_printf);//刚开始sem_printf没有资源,不能执行。
//在这里逆置执行完成之后,给sem_printf资源,打印就可以执行了
}
}
void* printf_msgbuf(void* arg)
{
msg_t *msg = (msg_t *)arg;
while(1){
sem_wait(&sem_printf); //sem_printf有资源,可以执行
printf("printf_msgbuf :***********\n");
printf("printf_msgbuf :%s\n",msg->buf);
sem_post(&sem_reverse); //sem_printf执行完成之后,给sem_reverse资源,
//那么sem_reverse就可以执行了
}
}
int main(int argc, const char *argv[])
{
msg_t msg = {"123456789",9};
pthread_t tid[2];
sem_init(&sem_reverse,0,1);//sem_reverse有一个资源
sem_init(&sem_reverse,0,0);//sem_reverse没有资源
pthread_create(&tid[0],NULL,reverse_msgbuf,(void *)&msg);
pthread_create(&tid[1],NULL,printf_msgbuf,(void *)&msg);
pause();
return 0;
}
3、信号量实现互斥操作
while(1){
sem_wait(&sem_printf);//消耗一个资源
printf("printf_msgbuf :***********\n");
printf("printf_msgbuf :%s\n",msg->buf);
sem_post(&sem_printf);//得到一个资源
}