Linux之线程-信号量sem_*

1. 概念

信号量可理解为进化版的互斥锁/量,允许多个线程访问共享资源。由于互斥锁的力度比较大,如果希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住。这样虽然达到了多线程操作共享数据时保证数据正确的目的,却无形中导致线程的并发性下降。线程从并行执行变成了串行执行,与直接使用单进程无异。信号量,是相对折中的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发。

2.主要应用函数

  • sem_init函数
  • sem_destroy函数
  • sem_wait函数
  • sem_trywait函数
  • sem_timedwait函数
  • sem_post函数

以上6个函数的返回值都是:成功返回0,失败返回-1,同时设置errno。(注意它们没有pthread前缀);

sem_t类型,本质仍然是结构体,但应用期间可简单看做为整数,忽略实现细节(类似于使用文件描述符);

sem_t sem;规定信号量sem不能<0,头文件#include<semaphore.h>。

3.信号量基本操作

sem_wait:  (1)信号量大于0,则信号量--自减,类比pthread_mutex_lock;(2)信号量等于0,造成线程阻塞。

      |

   对应

      |

sem_post: 信号量++自加,同时唤醒阻塞在信号量上的线程,类比pthread_mutex_unlock。

但,由于sem_t的实现对用户隐藏,所以所谓的++、--操作只能通过函数实现,而不能直接++、--符号运算。信号量的初值,决定了占用信号量的线程的个数。

4.具体函数

(1)sem_init函数

int sem_init(sem_t *sem, int pshared, unsigned int value);

作用:初始化一个信号量

参数:sem, 信号量;pshared,取0用于线程间,取非0(一般为1)用于进程间;value, 指定信号量的初值。

(2)sem_destroy函数

int sem_destroy(sem_t *sem);

作用:销毁一个信号量

(3)sem_wait函数

int sem_wait(sem_t *sem);

       int sem_trywait(sem_t *sem);

       int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

作用:给信号量加锁、尝试加锁,--。申请信号量,申请成功,value--。

sem_timedwait()与sem_wait()相同,不同之处在于abs_timeout指定了在无法立即执行减量操作时调用应阻塞的时间限制。 abs_timeout参数指向一个结构,该结构指定自1970年1月1日00:00:00 +0000(UTC)以来的绝对超时(以秒和纳秒为单位)。
        此结构定义如下:
            struct timespec {
                time_t tv_sec; / *秒* /
                long   tv_nsec; / *纳秒[0 .. 999999999] * /
            };
        如果超时已在调用时到期,并且信号无法立即锁定,则sem_timedwait()会失败,并显示超时错误(将errno设置为ETIMEDOUT)。
        如果可以立即执行该操作,则无论abs_timeout的值如何,sem_timedwait()都不会因超时错误而失败。 此外,在这种情况下不检查abs_timeout的有效性。

(4)sem_post函数

int sem_post(sem_t *sem);

作用:给信号量解锁,释放信号量value++。

5.实例--生产者与消费者模型

/*生产者与消费者模型,采用信号量'维持'正常有序运转*/

#include<stdio.h>
#include<unistd.h>
#include<semaphore.h>
#include<pthread.h>
#include<stdlib.h>

sem_t blank, xfull;
#define _SEM_CNT_ 5    //指定信号量的初值,定义一个全局变量表示

int queue[_SEM_CNT_];  //模拟生产者存放产品的“仓库”,生产者每生产1个产品,仓库空位blank减去1,消费者每从仓库消费1个产品,仓库空位置腾出来1个blank+1。
int beginnum = 100;    //产品编号从100开始计数

void *thr_producter(void *arg)
{
    int i=0;
    while (1)
    {
        sem_wait(&blank);                     //申请仓库中1个空位置资源,blank--
        printf("-------%s----self---%lu---num---%d\n",__FUNCTION__,pthread_self(),beginnum);
        queue[(i++)%_SEM_CNT_] = beginnum++;  //生产1个产品
        sem_post(&xfull);                     //xfull++,仓库占用位置+1
        sleep(rand()%3);
    }
    return NULL;
}

void *thr_customer(void *arg)
{
    int i=0;
    int num=0;
    while (1)
    {
        sem_wait(&xfull);                //消费者等待消费,仓库仓库占用位置腾出1个,xfull--
        num = queue[(i++)%_SEM_CNT_];    //消费
        printf("-------%s----self---%lu---num---%d\n",__FUNCTION__,pthread_self(),num);
        sem_post(&blank);                //消费完腾出一个空位置blank++给生产者继续生产
        sleep(rand()%3);
    }
    
    return NULL;
}

int main()
{
    sem_init(&blank,0,_SEM_CNT_);  //初始化信号量,生产者
    sem_init(&xfull,0,0);  //消费者一开始的初始化默认没有产品,也就是信号量初值value为0

    //创建线程
    pthread_t tid[2];
    pthread_create(&tid[0],NULL,thr_producter,NULL);
    pthread_create(&tid[1],NULL,thr_customer,NULL);

    //线程分离
    pthread_join(&tid[0],NULL);
    pthread_join(&tid[1],NULL);

    //销毁信号量
    sem_destroy(&blank);
    sem_destroy(&xfull);
    return 0;
}

输出:

~$ gcc sem_product.c -lpthread

~$ ./a.out
-------thr_producter----self---140089735280384---num---100
-------thr_customer----self---140089726887680---num---100
-------thr_producter----self---140089735280384---num---101
-------thr_customer----self---140089726887680---num---101
-------thr_producter----self---140089735280384---num---102
-------thr_customer----self---140089726887680---num---102
-------thr_producter----self---140089735280384---num---103
-------thr_customer----self---140089726887680---num---103
-------thr_producter----self---140089735280384---num---104
-------thr_customer----self---140089726887680---num---104
-------thr_producter----self---140089735280384---num---105
-------thr_customer----self---140089726887680---num---105

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值