信号量是一种特殊类型的变量,可以被增加和减少,但是对其的访问被保证是原子操作,就算是多线程中也是如此,这样如果多个地方都试图修改信号量的值,能够保证以此进行
二进制信号量只有0和1两种取值,而计数信号量可以设置更大的范围;通常用来保护一段代码,让每次只能被一个执行线程执行,直接通过二进制信号量可实现
线程中用到的信号量函数有下面几个
#include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value);
信号量创建函数;函数初始化sem指的信号量对象,设置它的共享选项,并且给它一个初始整数值;pshared参数控制信号量的类型,如果为0,表示这个信号量是当前进程的局部信号量,否则这个信号量就可以在多个进程之间共享,成功返回0,失败返回非0
#include <semaphore.h> int sem_wait(sem_t *sem); int sem_post(sem_t *sem); int sem_destroy(sem_t *sem);
这三个函数都是成功返回0,参数为一个指向由sem_init调用初始化的信号量的指针
sem_post函数以原子操作方式给信号量的值加1
sem_wait函数以原子操作的方式将信号量减1,但它会等待知道信号量有个非零值才会开始减法操作,比如对值为0的信号量调用sem_wait,这个函数就会等待,直到有其它线程增加了该信号量的值使得它不再为0,而假如两个线程同时在sem_wait调用上等待同一个信号量变为非零值,那么当该信号量被第三个线程增加1的时候,只有其中一个等待线程会对信号量减1,然后继续执行,而另一个线程还会继续等待
sem_destroy函数对用完后的信号量进行清理,并清理该信号量拥有的所有资源;如果准备清理的信号量正在被一些线程等待,就会收到一个错误
这些函数的定义看起来虽然简单,但是信号量具体的用处却没有互斥量那么直接和直观,其实用简单的话描述:比如一个线程中信号量为0,那么就会调用sem_wait阻塞,直到另一个线程发出信号sem_post加1之后,第一个线程就不阻塞,sem_wait就返回了,信号量再次变成0了
也就是说当信号量为0的时候,sem_wait就会休眠挂起调用的线程,知道信号量不为0为止;这样就很容易控制线程间的执行次序,一个唤醒另一个
下面小程序有两个线程,主线程不停获取用户输入,并且将message当作参数传给子线程,然后子线程打印,但这里添加了一个信号量,初始为0,假如主线程没有获取用户输入时,信号量还是为0,那么子线程的sem_wait就会阻塞,等待信号量非0,直到主线程接收到用户输入,然后通过sem_post改变了信号量,从而唤醒了子线程,进而打印
保证了两个线程,一个控制输入,一个控制输出,共享了message
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <semaphore.h> #define MAX_SIZE 1024 char message[MAX_SIZE]; sem_t a_sem; void *thread_function(){ sem_wait(&a_sem); while(strncmp("exit", message, 4)){ printf("You input %d characters\n", strlen(message) - 1); sem_wait(&a_sem); } } int main(){ pthread_t a_thread; if (sem_init(&a_sem, 0, 0)){ printf("Semaphore init failed!\n"); exit(1); } if (pthread_create(&a_thread, NULL, thread_function, NULL)){ printf("Create new thread failed!\n"); exit(1); } printf("Input as you like. Enter 'exit' to finish:\n"); while(strncmp("exit", message, 4)){ fgets(message, MAX_SIZE, stdin); sem_post(&a_sem); } printf("\nWaiting for thread to finish...\n"); if (pthread_join(a_thread, NULL)){ printf("Thread join failed!\n"); exit(1); } printf("Thread joined!\n"); sem_destroy(&a_sem); return 0; }
编译执行:
lihui@2015 ~ $ ./a.exe Input as you like. Enter 'exit' to finish: Hello World! You input 12 characters lihui You input 5 characters exit Waiting for thread to finish... Thread joined!