Linux线程同步(二)之使用信号量

时间:2021-05-13 15:15:23

信号量的概念

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);
}

运行结果:
Linux线程同步(二)之使用信号量