1.引言
信号量是包含一个非负整型变量,并且带有两个原子操作wait 和signal。wait 还可以被称为down、P 或lock,signal 还可以被称为up、V、unlock 或post。在Uinx的API中用的是wait和post。
如果信号量的非负整形变量S大于零,wait就将其减1,如果S 等于0,wait 就将调用线程阻塞。对于signal 操作,如果有线程在信号量上阻塞(此时S 等于0),signal就会解除对某个等待线程的阻塞,使其从wait 中返回,如果没有线程阻塞在信号量上,signal 就将S加1。
由此可见,S 可以被理解为一种资源的数量,信号量即是通过控制这种资源的分配来实现互斥和同步的。如果把S 设为1,信号量即可实现互斥量的功能。如果S 的值大于1,那么信号量即可使多个线程并发运行。另外,信号量不仅允许使用者申请和释放资源,而且还允许使用者创造资源,这就赋予了信号量实现同步的功能。可见,信号量的功能要比互斥量丰富许多。
POSIX信号量是一个sem_t 类型的变量,但POSIX 有两种信号量的实现机制:无名信号量和命名信号量。无名信号量可以用在共享内存的情况下,比如实现进程中各个线程之间的互斥和同步。命名信号量通常用于不共享内存的情况下,比如不共享内存的进程之间。
2.POSIX 无名信号量
在使用信号量之前,必须对其进行初始化。sem_init 函数初始化指定的信号量:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared,unsigned value);
//成功返回0,出错返回错误号
参数value 为信号量的初始值。参数pshared用于说明信号量的共享范围,如果pshared 为0,那么该信号量只能由初始化这个信号量的进程中的线程使用,如果pshared 非零,任何可以访问到这个信号量的进程都可以使用这个信号量。
函数sem_destroy 销毁一个指定的信号量,它的形式为:
#include <semaphore.h>
int sem_destroy(sem_t *sem);
//成功返回0,出错返回错误号
信号量的操作函数为sem_post、sem_wait、sem_trywait:
#include <semaphore.h>
int sem_post(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
//成功返回0,出错返回错误号
//sem_trywait函数试图获取信号量,但是如果获取不了,并不阻塞线程,与//pthread_mutex_trylock类似
这些函数同样可以使用与命名信号量!
3.POSIX 命名信号量
之所以称为命名信号量,是因为它有一个名字、一个用户ID、一个组ID和权限,这些是提供给不共享内存的那些进程使用命名信号量的接口。命名信号量的名字是一个遵守路径名构造规则的字符串。
sem_open函数用于创建或打开一个命名信号量:
#include <semaphore.h>
sem_t *sem_open(const char *name, intoflag);
参数 name 是一个标识信号量的字符串。参数oflag 用来确定是创建信号量还是连接已有信号量。如果设置了oflag 的O_CREAT 比特位,则会创建一个新的信号量。
sem_close函数用于关闭命名信号量:
#include <semaphore.h>
int sem_close(sem_t *sem);
//成功返回0,出错返回错误编号
单个程序可以用sem_close函数关闭命名信号量,但是这样做并不能将信号量从系统中删除,因为命名信号量在单个程序的执行之外是具有持久性的。当进程调用_exit、exit、exec 或从main 返回时,进程打开的命名信号量同样会被关闭。
sem_unlink函数用于在所有进程关闭了命名信号量之后,将信号量从系统中删除。它的形式为:
#include <semaphore.h>
int sem_unlink(const char *name);
//成功返回0,出错返回-1
4.问题
建两个线程,这两个线程各自将自己的一个整型变量i从1 递增到100,并通过信号量控制递增的过程,即这两个整型变量的差不能超过5。
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#define MAX 100
sem_t sem1,sem2;
void* th_fn1(void* arg)
{
inti;
for(i = 0; i < MAX; ++i)
{
sem_wait(&sem1);
printf("numberin thread1 is %d\n",i);
sem_post(&sem2);
}
pthread_exit((void*)"thread1exit\n");
}
void* th_fn2(void* arg)
{
inti;
for(i = 0; i < MAX; ++i)
{
sem_wait(&sem2);
printf("number in thread2 is %d\n",i);
sem_post(&sem1);
}
pthread_exit((void*)"thread2exit\n"); }
int main(void)
{
void*tret;
sem_init(&sem1,0,5);
sem_init(&sem2,0,5);
pthread_ttid1,tid2;
pthread_create(&tid1,NULL,th_fn1,NULL); pthread_create(&tid2,NULL,th_fn2,NULL);
pthread_join(tid1,&tret);
pthread_join(tid2,&tret);
sem_destroy(&sem1);
sem_destroy(&sem2);
return0;
}
5.生产者消费者问题
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#define MAX 100
#define BUFSIZE 10
//#define DEBUG 0
int buf[BUFSIZE]={0};
int in=0;
int out=0;
sem_t full,empty;
#ifdef DEBUG
void print(void)
{
int i;
printf("buf is:");
for(i=out;i<in;i++)
printf("%d ",buf[i]);
printf("\n");
}
#endif
void* producer(void* arg)
{
int i;
for (i = 1; i <= MAX; ++i)
{
/*produce*/
sem_wait(&full);
buf[in++]=i;
in%=BUFSIZE;
#ifdef DEBUG
print();
#endif
sem_post(&empty);
}
pthread_exit((void*)"thread1 exit\n");
}
void* comsumer(void* arg)
{
int i;
for (i = 1; i <= MAX; ++i)
{
/*comsumer*/
sem_wait(&empty);
out++;
out%=BUFSIZE;
sem_post(&full);
}
pthread_exit((void*)"thread2 exit\n");
}
int main(void)
{
void *tret;
sem_init(&full,0,10);
sem_init(&empty,0,0);
pthread_t tid_producer,tid_comsumer;
pthread_create(&tid_producer,NULL,producer,NULL);
pthread_create(&tid_comsumer,NULL,comsumer,NULL);
pthread_join(tid_producer,&tret);
printf("%s\n",(char*)tret);
pthread_join(tid_comsumer,&tret);
printf("%s\n",(char*)tret);
sem_destroy(&full);
sem_destroy(&empty);
return 0;
}