概念:
信号灯用来实现同步——用于多线程,多进程之间同步共享资源(临界资源)。信号灯分两种,一种是有名信号灯,一种是基于内存的信号灯。
有名信号灯,是根据外部名字标识,通常指代文件系统中的某个文件。
基于内存的信号灯,它主要是把信号灯放入内存的,基于内存的信号灯,同步多线程时,可以放到该多线程所属进程空间里;如果是同步多进程,那就需要把信号灯放入到共享内存中(方便多个进程访问)。
有名信号灯和基于内存的信号灯,具体区别体现在创建和销毁两个函数。有名信号灯使用sem_open和sem_close函数。基于内存的信号灯使用sem_init和sem_destroy函数。sem_init的参数可以控制是同步多线程,还是多进程;且该函数只能调用1次,因为调用后信号灯就存在了( 内存指针存在)。一般,使用基于内存的信号灯同步同进程多线程,使用有名信号灯同步多进程。
有名信号灯同步多线程:
1.sem_open函数。
功能:创建并初始化信号灯,如果存在就返回存在的信号灯。
头文件:#include函数原型:sem_t* sem_open(const char * name,int oflag,mode_t mode,unsigned intvalue);
或者:sem_t* sem_open(const char * name,intoflag);
参数:name是给信号灯指定一个名字。oflag的值为O_CREAT,表示如果信号灯不存在,创建信号灯;为O_CREAT|O_EXCL,如果信号灯不存在报错。后面两个参数,只有新建信号灯时使用。mode为信号灯的权限(0644),value为信号灯的值。
返回值:成功时,返回信号灯的指针,错误返回SEM_FAILED2.sem_close函数。
功能:关闭引用信号灯,信号灯引用计数减1。
头文件:#include函数原型:int sem_close(sem_t *sem)
参数:sem为信号灯的指针
返回值:成功时,返回0,失败,-1注:每个信号灯有一个引用计数器记录当前打开次数.关闭一个信号灯并没有将它从系统中删除,而是信号灯引用计数减13.sem_unlink函数
功能:信号灯引用计数为0时,从系统中删除信号灯。
头文件:#include函数原型:int sem_close(const char *name)
参数:name为信号灯的外部名字
返回值:成功时,返回0,失败,-1
4.sem_wait/sem_trywait函数
功能:等待共享资源,信号灯值为0就睡眠,信号灯值大于0,就使用共享资源,信号灯值减1。sem_trywait当信号灯值为0时,不睡眠,报错。
头文件:#include函数原型:int sem_wait(sem_t *sem),int sem_trywait(sem_t *sem)
参数:sem为信号灯指针
返回值:成功时,返回0,失败,-1
5.sem_getvalue函数
功能:获得信号灯的值
头文件:#include函数原型:int sem_getvalue(sem_t *sem,int *valp)
参数:sem为信号灯指针,valp为信号灯的值
返回值:成功时,返回0,失败,-1
6.sem_post函数
功能:使用完共享资源后,信号灯值加1,唤醒其他睡眠的。
头文件:#include函数原型:int sem_post(sem_t *sem)
参数:sem为信号灯指针
返回值:成功时,返回0,失败,-1
总结:
sem_wait在一个信号灯上等待(以锁住一个资源或等待一个时间),如果信号灯计数比0大,sem_wait函数将计数器减一并马上返回,否则,线程阻塞,一个阻塞的线程通过sem_post来发布一个信号灯(开锁一个资源或等待一个线程),
如果一个以上的线程在信号灯上等待,sem_post将唤醒其中一个(优先级最高的或最早等待的线程),如果没有线程在等待,信号灯计数器被加一。
假如有个代码段,期望最多有两个线程同时在里面执行(其他线程必须等待),可以使用一个信号灯而不需要附加任何状态,初始化信号灯值为2,然后再代码段开始调用sem_wait,结尾处调用sem_post,然后两个线程可以再信号登上等待而不被阻塞,
但是第三个线程将发现信号灯计数器为0,并且阻塞,每个系那成退出代码区时,他信号灯释放一个等待吸纳城(如果有的话恢复计数器)。
有名信号灯同步多进程:
unix网络编程第二卷,如是说不同进程(不管是否彼此有无亲缘关系),他们都可以访问同一个信号灯,只是需要在sem_open的时候传入的名字是一样就行。在有亲缘关系时,Posix中的fork如是描述,在父进程中打开的任何信号灯,仍应在子进程中打开。
示例代码:
/*semopen_pro.c*/#include#include#include#include#include#include
voidprint();
sem_t*sem;int main(int argc,char *argv[])
{int n=0,j;
pid_t pid;if(argc != 2)
{
printf("Usage:%s name.\n",argv[0]);
exit(0);
}//该信号灯不会因为不同进程而不同。
sem=sem_open(argv[1],O_CREAT,0644,3);while(n++<5)
{
pid=fork();if(pid == 0)
{
sem_wait(sem);
print();
sleep(1);
sem_post(sem);
printf("finish,the pid is %d\n",getpid());
exit(0);
}
}
j=0;//等待所有子进程退出
while(j++<5)
wait(NULL);
sem_close(sem);
sem_unlink(argv[1]);return 0;
}voidprint()
{intvalue;
printf("pid is %d, get the resource\n",getpid());
sem_getvalue(sem,&value);
printf("now,the semaphore value is %d\n",value);
}
[email protected]:/mnt/hgfs/C_libary# gcc -o semopen_pro semopen_pro.c
semopen_pro.c: In function ‘main’:
semopen_pro.c:19: warning: incompatible implicit declaration of built-infunction ‘exit’
semopen_pro.c:34: warning: incompatible implicit declaration of built-infunction ‘exit’/tmp/ccUECdL7.o: In function `main‘:
semopen_pro.c:(.text+0x5d): undefined reference to `sem_open‘semopen_pro.c:(.text+0x81): undefined reference to `sem_wait‘semopen_pro.c:(.text+0x9f): undefined reference to `sem_post‘semopen_pro.c:(.text+0x107): undefined reference to `sem_close‘semopen_pro.c:(.text+0x117): undefined reference to `sem_unlink‘/tmp/ccUECdL7.o: In function `print‘:
semopen_pro.c:(.text+0x14e): undefined reference to `sem_getvalue‘collect2: ld returned 1exit status
注:sem_XXX函数不是标准库函数,链接时需要指定库-lrt or -o semopen_pro semopen_pro.c
semopen_pro.c: In function ‘main’:
semopen_pro.c:19: warning: incompatible implicit declaration of built-infunction ‘exit’
semopen_pro.c:34: warning: incompatible implicit declaration of built-insemopen_pro sem
pidis 5262, getthe resource
now,the semaphore valueis 2pidis 5263, getthe resource
now,the semaphore valueis 1pidis 5264, getthe resource
now,the semaphore valueis 0finish,the pidis 5262pidis 5265, getthe resource
now,the semaphore valueis 0finish,the pidis 5263pidis 5266, getthe resource
now,the semaphore valueis 0finish,the pidis 5264finish,the pidis 5265finish,the pidis 5266
基于内存的信号灯同步多线程:
sem_init()
功能:初始化信号灯。
头文件:#include函数原型:int sem_open(sem_t * sem,int shared,unsigned intvalue);
参数:sem为信号灯指针,shared是指同步多线程还是多进程(0:多线程,其他:多进程),value为信号量值
返回值:成功时,返回0,失败时,返回-1sem_destroy()
功能:关闭信号
头文件:#include函数原型:int sem_destroy(sem_t *sem)
参数:sem为信号灯的指针
返回值:成功时,返回0,失败,-1
/*seminit_pth*/#include#include#include#include
voidprint();void * thread_function(void *arg);
sem_t sem;int main(int argc,char *argv[])
{int n=0;
pthread_t tid;//init semaphore
sem_init(&sem,0,3);while(n++<5)
{if((pthread_create(&tid,NULL,thread_function,NULL))!=0)
{
printf("can‘t create pthread.\n");
exit(0);
}
}
pthread_join(tid,NULL);
sem_destroy(&sem);return 0;
}void * thread_function(void *arg)
{
sem_wait(&sem);
print();
sleep(1); // sem_post(&sem);
printf("finish, pthread_id is %d\n",pthread_self());
}voidprint()
{intvalue;
printf("pthread_id is %d, get the resource\n",pthread_self());
sem_getvalue(&sem,&value);
printf("now,the semaphore value is %d\n",value);
}
[email protected]:/mnt/hgfs/C_libary# gcc -lrt -o seminit_pth seminit_pth.c
seminit_pth.c: In function ‘main’:
seminit_pth.c:21: warning: incompatible implicit declaration of built-infunction ‘exit’
seminit_pth.c: In function ‘thread_function’:
seminit_pth.c:35: warning: format ‘%d’ expects type ‘int’, but argument 2has type ‘pthread_t’
seminit_pth.c: In function ‘print’:
seminit_pth.c:41: warning: format ‘%d’ expects type ‘int’, but argument 2has type ‘pthread_t’
[email protected]:/mnt/hgfs/seminit_pth
pthread_idis -1224959120, getthe resource
now,the semaphore valueis 2pthread_idis -1233351824, getthe resource
now,the semaphore valueis 1pthread_idis -1241744528, getthe resource
now,the semaphore valueis 0finish, pthread_idis -1224959120finish, pthread_idis -1233351824finish, pthread_idis -1241744528pthread_idis -1250137232, getthe resource
now,the semaphore valueis 2pthread_idis -1216566416, getthe resource
now,the semaphore valueis 1finish, pthread_idis -1250137232finish, pthread_idis -1216566416
原文:https://www.cnblogs.com/tianzeng/p/9314463.html