linux多线程变成第四篇--信号量

信号量

信号灯与互斥锁和条件变量的主要不同在于"灯"的概念,灯亮则意味着资源可用,灯灭则意味着不可用。如果说后两中同步方式侧重于"等待"操作,即 资源不可用的话,信号灯机制则侧重于点灯,即告知资源可用;没有等待线程的解锁或激发条件都是没有意义的,而没有等待灯亮的线程的点灯操作则有效,且能保 持灯亮状态。当然,这样的操作原语也意味着更多的开销,而只有0和1两种取值的信号量叫做二进制信号量。

信号量相关函数

信号灯创建

int sem_init(sem_t *sem, int pshared, unsigned int value);  
该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。pshared控制信号量的类型,如果其值为0,就表示这个信号量是当前进程的局部信号量,否则信号量就可以在多个进程之间共享,value为sem的初始值。若value为0则是正常二进制信号。调用成功时返回0,失败返回-1.

信号灯点亮

int sem_post(sem_t *sem);
点灯操作将信号灯值原子地加 1,表示增加一个可访问的资源。

信号等熄灭

int sem_wait(sem_t *sem);

该函数用于以原子操作的方式将信号量的值减1。原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰

信号灯摧毁

int sem_destroy(sem_t *sem);  

被注销的信号灯 sem 要求已没有线程在等待该信号灯,否则返回-1,且置 errno 为 EBUSY。除此之外,LinuxThreads 的信号灯注销函数不做其他动作。

获取灯值

int sem_getvalue(sem_t * sem, int * sval)
读取 sem 中的灯计数,存于*sval 中,并返回 0。

理解:

sem_post函数的作用是给信号量的值加上一个“1”,它是一个“原子操作"即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同时对同一个文件进行读、加和写操作的两个程序就有可能会引起冲突。信号量的值永远会正确地加一个“2”--因为有两个线程试图改变它。

       sem_wait函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就会地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。
         信号量这种“只用一个函数就能原子化地测试和设置”的能力下正是它的价值所在。还有另外一个信号量函数sem_trywait,它是sem_wait的非阻塞搭档。

信号量的实例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h> //包含线程相关头文件
#include <errno.h>
#include <sys/ipc.h>
#include <semaphore.h> //包含信号量相关头文件
int lock_var;
time_t end_time;
sem_t sem1,sem2; //声明两个信号量
 
void pthread1(void *arg); //声明两个线程函数
void pthread2(void *arg);
 
int main(int argc, char *argv[])
{
    pthread_t id1,id2; //声明两个线程
    pthread_t mon_th_id;
    int ret;
    end_time = time(NULL)+30;
    ret=sem_init(&sem1,0,1); //对信号量进行初始化,第一个0表示此信号量子整个进程中共享,第二个1表示信号量初始值
    ret=sem_init(&sem2,0,0);//对信号量的初始化,信号量的值为0
    if(ret!=0)
    {
        perror("sem_init");
    }
    ret=pthread_create(&id1,NULL,(void *)pthread1, NULL); //创建线程
    if(ret!=0)
        perror("pthread cread1");
    ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);
    if(ret!=0)
        perror("pthread cread2");
    pthread_join(id1,NULL); //用来等待线程1的结束
    pthread_join(id2,NULL); //用来等待线程2的结束
    exit(0);
}
 
void pthread1(void *arg) //线程1的执行内容
{
    int i;
    while(time(NULL) < end_time){
        sem_wait(&sem2); //线程阻塞一直等到sem2信号量大于0,执行后将sem2减1,代表资源已经被使用
        for(i=0;i<2;i++){
            sleep(1);
            lock_var++;
            printf("lock_var=%d\n",lock_var);
        }
        printf("pthread1:lock_var=%d\n",lock_var);
        sem_post(&sem1); //将信号量sem1的值加1,代表资源增加
        sleep(1);
    }
}
 
void pthread2(void *arg)
{
    int nolock=0;
    int ret;
    while(time(NULL) < end_time){
    sem_wait(&sem1);
    printf("pthread2:pthread1 got lock;lock_var=%d\n",lock_var);
    sem_post(&sem2);
    sleep(3);
    }
} 

注释:主线成创建了两个子线程,线程函数分别为pthread1与pthread2,pthread1中的由于信号灯sem2的值为0所以此时该线程被阻塞,直到别的线程将此信号灯的值加1此线程才会被唤醒继续运行,因此pthread2率先执行,在此线程函数中一开始遇到sem_wait,但是由于信号灯sem1初始值为1因此继续运行该线程函数,直到运行到sem_post(&sem2)另外一个线程被唤醒继续运行,pthread2休眠3秒。如此依次交替运行直到30秒结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值