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
    评论
在嵌入式Linux平台下,多线程之间的同步和互斥可以使用信号量来实现。信号量是一种多线程间通信的机制,用于控制多个线程对共享资源的访问。 使用信号量需要引入头文件#include <semaphore.h>。 信号量的基本操作有三个函数: 1. sem_init():初始化信号量,设置信号量的初值。 2. sem_wait():等待信号量,如果信号量值为0,则线程阻塞等待,否则信号量值减1,线程继续执行。 3. sem_post():释放信号量,将信号量值加1。 下面是一个使用信号量实现线程同步的例子: ```c #include <pthread.h> #include <semaphore.h> #include <stdio.h> sem_t sem; //定义信号量 int count = 0; //共享资源 void *thread_func(void *arg) { sem_wait(&sem); //等待信号量 count++; //对共享资源进行操作 printf("Thread %d: count = %d\n", *(int *)arg, count); sem_post(&sem); //释放信号量 pthread_exit(NULL); } int main() { pthread_t thread[5]; int i, id[5]; sem_init(&sem, 0, 1); //初始化信号量 for (i = 0; i < 5; i++) { id[i] = i; pthread_create(&thread[i], NULL, thread_func, &id[i]); } for (i = 0; i < 5; i++) { pthread_join(thread[i], NULL); } sem_destroy(&sem); //销毁信号量 return 0; } ``` 上面的例子中,共有5个线程同时对count变量进行操作,但是通过信号量的使用,保证了每次只有一个线程能够访问共享资源。信号量sem的初值为1,表示有一个线程能够访问共享资源,其他线程需要等待。每个线程在访问共享资源之前都需要等待信号量sem,如果sem为0,则线程阻塞等待,否则sem减1,线程访问共享资源。当一个线程访问完共享资源之后,需要释放信号量sem,将sem加1,其他线程就可以访问共享资源了。最后,销毁信号量sem。 需要注意的是,在使用信号量时,要保证信号量的使用是线程安全的,否则会导致竞争条件的发生。在多线程环境下,使用信号量还需要考虑死锁的问题,避免出现死锁现象。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值