信号量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()。

CMSIS-RTOS 信号量的更多相关文章

  1. CMSIS RTOS -- embOS segger

    #ifndef __CMSIS_OS_H__ #define __CMSIS_OS_H__ #include <stdint.h> #include <stddef.h> #i ...

  2. RTOS之CMSIS-RTOS

    CMSIS-RTOS 是实时操作系统的通用 API.它提供了标准化的编程接口,它只是封装了RTX/embos,以后还可能封装freeRTOS,uc/os等等第三方OS,CMSIS RTOS是ARM现在 ...

  3. CMSIS_RTOS_Tutorial自译中文版

    一.序言 本资料是Trevor Martin编写的<The Designers Guide to the Cortex-M Processor Family>的摘要,并得到Elsevier ...

  4. ARM官方《CMSIS-RTOS教程》之线程Threads

    创建线程Creating Threads 一旦RTOS开始运行,就会有很多系统调用来管理和控制活跃的线程.默认情况下,main()函数自动被创建为第一个可运行的线程.在第一个例子里我们使用main() ...

  5. STM32F429i-DISCO FreeRTOS keil STM32CubeMX

    目标: 在STM32F429 Disco开发板上用FreeRTOS双线程点亮双闪led. 准备: 0. STM32F429i-DISCO 1. keil ARMMDK 5.13 2. STM32Cub ...

  6. CMSIS-RTOS 时间管理之时间延迟Time Delay

    时间管理 Time Management 此RTOS除了可以把你的应用代码作为线程运行,它还可以提供一些时间服务功能,使用这些功能你就可以访问RTOS的一些系统调用. 时间延迟Time Delay 在 ...

  7. CMSIS-RTOS的使用

    CMSIS-RTOS实现通常作为库提供.要将RTOS功能添加到现有的基于CMSIS的应用程序,需要添加RTOS库(通常是配置文件).RTOS库的可用功能在头文件cmsis_os.h中定义,该文件特定于 ...

  8. 聊聊CMSIS-RTOS是什么东东

    起因:发布自己翻译用的CMSIS_RTOS_Tutorial后,陆续收到网友关于“CMSIS-RTOS是干么的?”之类的问题,再次统一回复. 众所周知,实时操作系统是嵌入式领域的基石.而可选的嵌入式操 ...

  9. 【机翻】RTnet – 灵活的硬实时网络框架

    目录 RTnet – 灵活的硬实时网络框架 0 摘要 1 介绍 2 基础服务 2.1 数据包管理 2.2 UDP/IP 实现 2.3 Driver Layer 2.4 应用程序接口 2.5 捕获扩展 ...

  10. 优先级反转实验,使用信号量实现【RT-Thread学习笔记 5】

    RTOS中很经典的问题.就是在使用共享资源的时候,优先级低的进程在优先级高的进程之前执行的问题.这里模拟这种情况. 下面的实验模拟了优先级反转的情况: 先定义三个线程: //优先级反转实验 rt_se ...

随机推荐

  1. html5plus 从相册选择图片后获取图片的大小

    plus.gallery.pick(function (filePath) { plus.io.resolveLocalFileSystemURL(filePath, function (entry) ...

  2. Maven配置文件中配置指定JDK版本

    1. 在setting.xml文件中的<profiles>标签加入如下配置: <profile> <id>jdk-1.8</id> <activa ...

  3. 我所认识的EXT2(一)

    前言: 本文是笔者自己在学习文件系统中的一些体会,写出来和大家分享一下.本文首先是介绍了下文件系统的一些理论概念,然后分析了ext2文件系统的原理和部分源码. 文件系统是什么: 人们在认识一件陌生事物 ...

  4. ActiveMQ学习笔记(21)----ActiveMQ集成Tomcat

    1. 监控和管理Broker Web Console 方式:直接访问ActiveMQ的管理页面:http://localhost:8161/admin,默认的用户名和密码是admin/admin.具体 ...

  5. GCD - Extreme (II) UVA - 11426 欧拉函数_数学推导

    Code: #include<cstdio> using namespace std; const int maxn=4000005; const int R=4000002; const ...

  6. django框架-DRF视图中的request和response

    1.Request 相对于django框架,DRF工程的request类则是继承自HttpRequest类,Rest framework提供了parser解释器,用来解释请求中的content_typ ...

  7. python字典对象的update()方法

    使用字典对象的update()方法,如A.update(B),将B字典的键值对一次性全部添加到A字典对象,当A字典为空时,相当于深复制,非常方便.如果两个字典中存在相同的键,则进行值的更新. A={} ...

  8. Eclipse集成Git插件及使用

    目录(?)[+] 1 Git插件安装 11 下载插件 2 Git提交代码 21 建git的本地仓库 第一种 第二种 项目创建本地仓库后 如图 22 提交本地仓库 23 提交远程仓库 3 Git下载代码 ...

  9. Hdu4786

    Fibonacci Tree Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  10. HDU 5297 Y sequence Y数列

    题意:给定正整数n和r.定义Y数列为从正整数序列中删除全部能表示成a^b(2 ≤ b ≤ r)的数后的数列,求Y数列的第n个数是多少. 比如n = 10. r = 3,则Y数列为2 3 5 6 7 1 ...