POSIX多线程笔记(7):信号量(Semaphore)

Source

信号量的概念

1965年,E.W.Dijkstra提出了信号量的概念,之后信号量即成为操作系统实现互斥和同步的一种普遍机制。信号量是包含一个非负整型变量,并且带有两个原子操作wait和signal。wait还可以被称为down、P或lock,signal还可以被称为up、V、unlock或post。

如果信号量的非负整形变量S大于零,wait就将其减1,如果S等于0,wait就将调用线程挂起。对于signal操作,如果有线程在信号量上阻塞(此时S等于0),signal就会解除对某个等待线程的阻塞,使其从wait中返回,如果没有线程阻塞在信号量上,signal就将S加1。

由此可见,S可以被理解为一种资源的数量,信号量即是通过控制这种资源的分配来实现互斥和同步的。如果把S设为1,信号量即可实现互斥量的功能。如果S的值大于1,那么信号量即可使多个线程并发运行

POSIX信号量是一个sem_t类型的变量,但POSIX有两种信号量的实现机制:无名信号量命名信号量。无名信号量可以用在共享内存的情况下,比如实现进程中各个线程之间的互斥和同步。命名信号量通常用于不共享内存的情况下,比如不共享内存的进程之间。

信号量相关的函数和数据结构需要引用头文件semaphore.h

POSIX无名信号量

在使用信号量之前,必须对其进行初始化。sem_init函数初始化指定的信号量,它的形式为:

int sem_init(sem_t *sem, int pshared, unsigned value); 

参数sem指向要初始化的信号量,参数value为信号量的初始值。参数pshared用于说明信号量的共享范围,如果pshared为0,那么该信号量只能由初始化这个信号量的进程中的线程使用,如果pshared非零,任何可以访问到这个信号量的进程都可以使用这个信号量。如果成功,sem_init返回0,如果不成功,sem_init返回-1并设置errno。

函数sem_destroy销毁一个指定的信号量,它的形式为:

int sem_destroy(sem_t *sem); 

参数sem为指向要销毁的信号量的指针。如果成功,sem_destroy返回0,如果不成功,sem_destroy返回-1并设置errno。如果*sem不是有效的信号量,sem_destroy就将errno置为EINVAL。

sem_post函数实现对指定信号量的signal操作,它的形式为:

int sem_post(sem_t *sem); 

如果成功,sem_post返回0。如果不成功,sem_post返回-1并设置errno。如果*sem不是有效的信号量,sem_post就将errno置为EINVAL。

sem_wait函数实现对指定信号量的wait操作。sem_trywait函数与sem_wait类似,只是在试图对一个为零的信号量进行操作时,它不会阻塞调用线程,而是立即返回,类似于互斥量操作中的pthread_mutex_trylock()。这两个函数的形式为:

int sem_wait(sem_t *sem); 
int sem_trywait(sem_t *sem);

如果成功,这两个函数返回0,如果不成功,这些函数返回-1并设置errno。如果*sem不是有效的信号量,sem_ wait就将errno置为EINVAL。如果sem_trywait在信号量为零时执行wait操作,则将errno置为EAGAIN。

sem_post,sem_wait和sem_trywait同样可用于命名信号量。

信号量的实例

下面是一个无名信号量的应用实例,它完成的工作为:创建两个线程,这两个线程各自将自己的一个整型变量i从1递增到100,并通过信号量控制递增的过程,即这两个整型变量的差不能超过5。

#include <stdio.h> 
#include <pthread.h> 
#include <semaphore.h>
 
#define UPBOUND 100 
sem_t sem1; 
sem_t sem2; 

void *threadfunc1(void *pvoid) 
{
	int i = 0;
	while(i < UPBOUND)
	{
		sem_wait(&sem1);
		i++;
		printf("The integer of child thread1 is %d now.\n", i); 	
		sem_post(&sem2);			
	}		
}

void *threadfunc2(void *pvoid) 
{
	int i = 0;
	while(i < UPBOUND)
	{
		sem_wait(&sem2);
		i++;
		printf("The integer of child thread2 is %d now.\n", i); 	
		sem_post(&sem1);			
	}		
}
int main()
{
	pthread_t tid1, tid2;
	sem_init(&sem1, 0, 5);		
	sem_init(&sem2, 0, 5);
	
	printf("Semaphores are initialized.\n");
	pthread_create(&tid1, NULL, &threadfunc1, NULL);
	pthread_create(&tid2, NULL, &threadfunc2, NULL);
	
	printf("Threads are started.\n");
	
	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);
	printf("Threads finished.\n");
	
	sem_destroy(&sem1);
	sem_destroy(&sem2);
	
	printf("Main thread finished\n"); 
	
	return 0;
}

运行结果:
在这里插入图片描述