多线程编程---信号量

信号量也就是操作系统中所用到的 PV 原子操作,它广泛用于进程或线程间的同步与互斥。
信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
PV原子操作的工作原理:
PV原子操作是对整数计数器信号量sem的操作。一次P操作使sem减一,而一次V操作使sem加一。
进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。当信号量sem的值大于等于零时,该进程(或线程)具有公共资源的访问权限;相反,当信号量sem的值小于零时,该进程(或线程)就将阻塞直到信号量sem的值大于等于0为止。 

PV 原子操作主要用于进程或线程间的同步和互斥这两种典型情况。若用于互斥,几个进程(或线程)往往只设置一个信号量 sem,当信号量用于同步操作时,往往会设置多个信号量,并安排不同的初始值来实现它们之间的顺序执行,

    信号量互斥操作                                                                       信号量同步操作



函数说明。 
Linux 实现了 POSIX 的无名信号量,主要用于线程间的互斥与同步。这里主要介绍几个常见函数。  
sem_init()用于创建一个信号量,并初始化它的值。  
sem_wait()和 sem_trywait()都相当于 P 操作,在信号量大于零时它们都能将信号量的值减一,两者的区别在于若信号量小于零时,sem_wait()将会阻塞进程,而 sem_trywait()则会立即返回。  
sem_post()相当于 V 操作,它将信号量的值加一同时发出信号来唤醒等待的进程。 
sem_getvalue()用于得到信号量的值。 
sem_destroy()用于删除信号量。



demo1: 用信号量同步机制实现3个线程之间的有序执行,只是执行顺序是跟创建线程的顺序相反,具体代码思路是让3个线程重用同一个执行函数。每个线程都有三次循环(可以看成3个小任务),每次循环之间等待1~10s的时间,意义在于模拟每个任务的到达时间是随机的,没有任何特定的规律。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>

#define THREAD_NUMBER      3
#define REPEAT_NUMBER      3
#define DELAY_TIME_LEVELS  10.0

//定义一个信号量数组 设置多个信号量用于线程的同步操作
sem_t sem[THREAD_NUMBER];

void *thrd_func(void *arg)
{
	int thrd_num = (int)arg;
	int delay_time = 0;
	int count = 0;
	
	sem_wait(&sem[thrd_num]);//相当信号量大于0时减一,信号量小于0时阻塞线程
	
	printf("Thread %d is starting\n", thrd_num);
	
	for(count = 0; count < REPEAT_NUMBER; count++)
	{
		//模拟循环等待时间
		delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
		
		sleep(delay_time);
		
		printf("Thread %d: job %d delay = %d\n", thrd_num, count, delay_time);
	}
	
	printf("Thread %d finished\n", thrd_num);
	
	pthread_exit(NULL);
}

int main(void)
{
	pthread_t thread[THREAD_NUMBER];
	int no = 0, res;
	void* thrd_ret;
	
	srand(time(NULL));
	
	for(no = 0; no < THREAD_NUMBER; no++)
	{
		sem_init(&sem[no], 0, 0);//用于创建一个信号量,并初始化它的值
		res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);
		
		if(res != 0)
		{
			printf("Create thread %d failed\n", no);
			
			exit(res);
		}
	}

	printf("Create treads success\n Wating for threads to finish...\n");
	
	//将信号量的值加一同时发出信号来唤醒等待的进程
	sem_post(&sem[THREAD_NUMBER - 1]);
	
	for(no = THREAD_NUMBER - 1; no >= 0; no--)
	{
		res = pthread_join(thread[no], &thrd_ret);//阻塞等待线程结束
		if(!res)
		{
			printf("Thread %d joined\n", no);
		}
		else
		{
			printf("Thread %d join fail\n", no);
		}
		//相当于V操作,它将信号量的值加1同时发出信号唤醒等待的线程
		sem_post(&sem[(no + THREAD_NUMBER - 1) % THREAD_NUMBER]);
	}
	
	for(no = 0; no < THREAD_NUMBER; no++)
	{
		sem_destroy(&sem[no]);//删除信号量
	}
	
	
	return 0;
}

运行效果截图:





  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值