信号量的概念
1、信号量是一个特殊类型的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作,即使在一个多线程程序中也是如此。这意味着如果一个程序中有两个(或更多)的线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。但如果是普通变量,来自同一程序中不同线程的冲突操作所导致的结果将是不确定的。
2、最简单的信号量是二进制信号量,它只有0和1两种取值。还有更通用的信号量——计数信号量,它可以有更大的取值范围。信号量一般常用来保护一段代码,使其每次只能被一个执行线程运行,要完成这个工作,就要使用二进制信号量。有时,我们希望可以允许有限数目的线程执行一段指定的代码,这就需要计数信号量。
信号量的使用
信号量函数的名字都以sem_开头,信号量类型是sem_t,线程中使用的基本信号量函数有4个。分别是:
1、信号量创建函数
#include <semaphore.h>
int sem_init(sem_t *sem,int pshared,usigned int value);
这个函数初始化sem指向的信号量对象,设置它的共享选项,并给它一个初始化的整数值。pshared参数控制信号量的类型,如果其值为0,就表示这个信号量是当前进程的局部信号量,否则,这个信号量就可以在多个进程之间共享。(Linux还不支持这种共享,给pshared参数传递一个非零值将导致调用失败???)
2、信号量控制函数
#include <semaphore.h>
int sem_wait(sem_t *sem);//相当于p操作
int sem_trywait(sem_t *sem);//sem_wait的非阻塞版本
int sem_post(sem_t *sem);//相当于v操作
(1)sem_post函数的作用是以原子操作方式给信号量加1.所谓原子操作是指,如果两个线程企图同时给一个信号量加1,它们之间不会互相干扰,而不像如果两个程序同时对同一个文件进行读取、增加、写入操作时可能会引起冲突。信号量的值总是会被正确加2,因为有两个线程试图改变它。
(2)sem_wait函数以原子操作的方式将信号量的值减1,但它会等待直到信号量有个非零值才会开始减法操作。因此,如果对值为2的信号量调用sem_wait,线程将继续进行,但信号量的值会减到1.如果对值为0的信号量调用sem_wait,这个函数就会等待,直到有其他线程增加了该信号量的值使其不再是0为止。如果两个线程同时在sem_wait调用上等待同一个信号量变为非零值,那么当该信号量被第三个线程增加1时,只有其中一个等待线程将开始对信号量减1,然后继续执行,另外一个线程还将继续等待。
(3)sem_trywait函数是sem_wait的非阻塞版本,sem_trywait()函数仅在信号量当前没有锁定的情况下(也就是说,如果信号量值是正的。),锁定由sem所引用的信号量;否则,它就不能锁定信号量。
如果调用进程成功地执行了由sem指定的信号量锁操作,那么sem_trywait()和sem_wait()函数将返回零。如果调用不成功,信号量的状态将保持不变,函数将返回-1的值,并设置errno来指示错误。
3、信号量清理函数
#include <semaphore.h>
int sem_destroy(sem_t *sem);
与前几个函数一样,这个函数也以一个信号量指针为参数,并清理该信号量拥有的所有资源。如果企图清理的信号量正在被一些线程等待,就会收到一个错误。函数在成功时会返回0。
信号量的举例
例1:主线程负责接收用户输入,函数线程统计用户输入的字符个数。(用信号量实现)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
char buf[128] = {0};
sem_t sem;
//函数线程
void * fun_pthread1(void *arg)
{
while(1)
{
sem_wait(&sem);
if(strncmp(buf,"end",3) == 0)
{
break;
}
int count = 0;
while(1)
{
if(buf[count] == 0 || buf[count] == '\n')
{
break;
}
count++;
}
printf("count:%d\n",count);
sem_post(&sem);
sleep(1);
}
}
//主线程
void main()
{
sem_init(&sem,5,1);
sem_wait(&sem);
pthread_t id;
pthread_create(&id,NULL,fun_pthread1,NULL);
while(1)
{
printf("请输入\n");
fgets(buf,128,stdin);
sem_post(&sem);
if(strncmp(buf,"end",3)==0)
{
break;
}
sleep(1);
sem_wait(&sem);
}
pthread_join(id,NULL);
sem_destroy(&sem);
pthread_exit(NULL);
}
运行结果: