• CMSIS-RTOS 信号量


    信号量Semaphores

    和信号类似,信号量也是一种同步多个线程的方式,简单来讲,信号量就是装有一些令牌的容器。当一个线程在执行过程中,就可能遇到一个系统调用来获取信号量令牌,如果这个信号量包含多个令牌,线程就会继续执行,同时信号量令牌的数量就会减一。如果此时信号量中没有令牌,线程就会被置于等待状态,直到出现一个可用的令牌。在线程执行的任何位置,它都可以给信号量增加一个令牌。

    这里写图片描述

    信号量用来帮助访问程序资源,在一个线程允许访问一个信号量之前,它必须拥有一个令牌。如果没有令牌可用,它就必须等待,当线程使用完资源时,它就必须释放令牌。

    上图揭示了两个线程如何使信号量同步。首先,必须创建一个信号量,并初始化令牌数目,在上图中,信号量初始化令牌数目为1。当两个线程运行到某一点时就试图从信号量中请求一个令牌,图中第一个线程到达这个点,成功获取一个令牌,然后继续执行,第二个线程也试图获取一个令牌,但是当前信号量为空,所以它暂停执行,并进入等待状态,直到信号量中有令牌可用。

    与此同时,执行中的线程可以释放令牌给信号量,一旦释放完成,等待中的线程就会获取令牌,并离开等待状态进入准备状态。紧接着调度器就会把它调度到运行状态去执行剩下的代码。

    因为信号量保含较多的系统调用,所以想一次性全部理解有些难度,在本节,我们将首先看看如何给系统添加信号量,然后了解一下常用的信号量应用。

    在使用信号量之前,你必须先声明一个信号量容器:

    osSemaphoreId sem1;
    osSemaphoreDef(sem1);

    然后在线程里给信号量容器初始化一些令牌:

    sem1 = osSemaphoreCreate(osSemaphore(sem1), SIX_TOKENS);

    有一点比较重要,就是在线程运行的过程中令牌既可以被创建也可以被销毁,举个例子,你可以初始化一个信号量,拥有0个令牌,然后用一个线程给这个信号量创建一些令牌,再使用另一个线程移除它们,这样一来,你就可以设计线程,既可以充当生产者的线程,也可以充当消费者的线程。

    一旦信号量被创建,令牌就可能被获取,并以类似事件标志的方式发送给信号量,os_sem_wait调用来阻塞线程,直到有信号量令牌可用,类似os_event_wait,当然,在这个调用中同样拥有超时机制,超时初始值是0xFFFF。

    osStatus osSemaphoreWait(osSemaphoreId semaphore_id, uint32_t millisec);

    一旦线程完成对信号量资源的使用,它就可以给信号量容器发送一个令牌:

    osStatus osSemaphoreRelease(osSemaphoreId semaphore_id);

    信号量的使用Using Semaphores

    前面说过信号量包含较多的OS调用,所以它拥有广泛的同步应用,这也就导致了它可能是RTOS里面最难理解的部分。在本节,我们将看看信号量的一些普遍应用,这些应用摘自“信号量小书”(Allen B. Downey著),这本书可以免费下载,链接在本书的最后部分。

    发送信号Signaling

    两个线程之间的同步是信号量最简单的使用方式:

    osSemaphoreId sem1;
    osSemaphoreDef(sem1);
    void thread1 (void)
    {
        sem1 = osSemaphoreCreate(osSemaphore(sem1), 0); 
        while(1)
            { 
                FuncA();
                osSemaphoreRelease(sem1)
            }
    }
    void thread2 (void)
    {
        while(1)
            {
                osSemaphoreWait(sem1,osWaitForever)
                FuncB();
            }
    }

    复用Multiplex

    复用是用来限制访问临界代码区的线程个数,举个例子,对于存储空间的访问仅仅可能只支持几个受限的调用。

    osSemaphoreId multiplex;
    osSemaphoreDef(multiplex);
    void thread1 (void)
    {
        multiplex =osSemaphoreCreate(osSemaphore(multiplex), FIVE_TOKENS); 
        while(1) { 
            osSemaphoreWait(multiplex,osWaitForever)
            ProcessBuffer(); 
            osSemaphoreRelease(multiplex); 
        }
    }

    在这个例子中,我们给复用信号量初始化了5个令牌,当一个线程要调用ProcessBuffer()函数时,就必须首先获取信号量令牌,一旦此函数结束,令牌必须归还给信号量。如果超过5个线程试图调用ProcessBuffer()函数,第六个线程就必须等待其中一个线程完成ProcessBuffer()函数,并归还令牌。就这样,复用信号量确保了最多只能有5个线程可以“同时”调用ProcessBuffer()函数。

     

    交汇Rendezvous

    一种更常规的信号量通信形式叫做交汇,交汇确保两个线程同时到达某个确定的执行点,除非两个线程都到达这个交汇点,否则它们都不会继续运行。

    osSemaphore arrived1, arrived2;
    osSemaphoreDef(arrived1);
    osSemaphoreDef(arrived2);
    
    void thread1(void){
        Arrived1 = osSemphoreCreate(osSemphore(arrived1),ZERO_TOKENS);
        Arrived2 = osSemphoreCreate(osSemphore(arrived2),ZERO_TOKENS);
        while(1){
            FuncA1();
            osSemphoreRelease(Arrived1);
            osSemphoreWait(Arrived2, osWaitForever);
            FuncA2();
        }
    }
    
    void thread2(void){
        while(1){
            FuncB1();
            os_sem_send(Arrived2);
            os_sem_wait(Arrived, osWaitForever);
            FuncB2();
        }
    }

    上面的例子中,两个信号量会确保两个线程发生交汇,然后各自执行FuncA2()和FuncB2()。

  • 相关阅读:
    tomcat解决 java.lang.IllegalArgumentException: Request header is too large
    mysql之表操作
    类的封装
    classmethod和staticmethod
    13-轮播实现(各种)
    12-事件委托(事件代理)
    python基础篇笔记03 迭代器、生成器、推导式、内置函数
    python基础篇笔记02 文件操作 序列解包**星号语法
    Django自定制分页器Pagination
    Ajax相关 及 解决csrf_token、Forbidden(CSRF)问题
  • 原文地址:https://www.cnblogs.com/zzdbullet/p/9547507.html
Copyright © 2020-2023  润新知