信号量线程同步

信号量是一种特殊类型的变量,可以被增加和减少,但是对其的访问被保证是原子操作,就算是多线程中也是如此,这样如果多个地方都试图修改信号量的值,能够保证以此进行

二进制信号量只有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!

发表评论