Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread
RT-Thread-API
内核
RT-Thread 隐藏的宝藏之等待队列
发布于 2021-02-23 23:58:21 浏览:1553
订阅该版
[tocm] ### 1. 等待队列是什么 等待队列是一个轻量级的线程间异步通讯方式。 他有两个特点: 1. 轻量: `API` 较少 2. 异步: ### 2. 等待队列怎么使用 用户只需要使用其中的五个 `API` 就可以了。 1. 初始化等待队列 `rt_inline void rt_wqueue_init(rt_wqueue_t *queue)` queue : 消息队列控制块 2. 加入等待队列 `int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec)` queue : 等待队列的控制块 condition :这个形参没有使用到, 必须传递 `0` msec : 需要等待的时间,单位是毫秒 3. 唤醒等待队列 `void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key)` queue : 等待队列的控制块 key : 唤醒条件,目前源码中未使用 4. 在等待队列中插入一个节点 `void rt_wqueue_add(rt_wqueue_t *queue, struct rt_wqueue_node *node)` queue : 等待队列控制块 node : 插入队列的节点 5. 从等待队列移除一个节点 `void rt_wqueue_remove(struct rt_wqueue_node *node)` node : 需要移除的节点 ### 3. 等待队列的实现 1. 初始化等待队列 ```c rt_inline void rt_wqueue_init(rt_wqueue_t *queue) // 这里使用了关键字 inline { RT_ASSERT(queue != RT_NULL); // 断言检查 queue->flag = RT_WQ_FLAG_CLEAN; // 设置标志位 rt_list_init(&(queue->waiting_list)); // 初始化链表 } ``` 2. 加入等待队列 ```c int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec) { int tick; rt_thread_t tid = rt_thread_self();// 获取当前调用等待队列的线程 rt_timer_t tmr = &(tid->thread_timer);// 获取当前线程的定时器 struct rt_wqueue_node __wait;// 等待队列的节点 rt_base_t level; /* current context checking */ RT_DEBUG_NOT_IN_INTERRUPT; tick = rt_tick_from_millisecond(msec);// 获取传递进来的时间的转换成 tick,所以超时时间的单位是 ms if ((condition) || (tick == 0)) // condition 必须传递 0 return 0; __wait.polling_thread = rt_thread_self(); //获取当前线程给到 __wait __wait.key = 0; // key 赋值 0 __wait.wakeup = __wqueue_default_wake; // 这个函数建议用户自己实现一个,默认的这个会返回0 rt_list_init(&__wait.list); // 初始化链表 level = rt_hw_interrupt_disable(); // 关中断 if (queue->flag == RT_WQ_FLAG_WAKEUP) // 如果队列已经是唤醒状态了 { /* already wakeup */ goto __exit_wakeup; } rt_wqueue_add(queue, &__wait); // 把 __wait 插入到 queue-> waiting_list 的前面 rt_thread_suspend(tid);// 挂起这个线程 /* start timer */ if (tick != RT_WAITING_FOREVER) // 启动线程的定时器 { rt_timer_control(tmr, RT_TIMER_CTRL_SET_TIME, &tick); rt_timer_start(tmr); } rt_hw_interrupt_enable(level);// 开中断 rt_schedule(); // 进行一次调度 level = rt_hw_interrupt_disable(); // 关中断 __exit_wakeup: queue->flag = 0; // 设置 FLAG ,这个用宏的方式可能更优雅 rt_hw_interrupt_enable(level); // 开中断 rt_wqueue_remove(&__wait); // 从等待队列中移除 __wait 这个节点 return 0; } ``` 3. 唤醒等待队列 ```c void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key) { rt_base_t level; register int need_schedule = 0; rt_list_t *queue_list; struct rt_list_node *node; struct rt_wqueue_node *entry; queue_list = &(queue->waiting_list); level = rt_hw_interrupt_disable(); // 关中断 /* set wakeup flag in the queue */ queue->flag = RT_WQ_FLAG_WAKEUP; // 设置等待队列的标志位 if (!(rt_list_isempty(queue_list))) // 检查链表是否为空 { for (node = queue_list->next; node != queue_list; node = node->next)// 循环遍历 { entry = rt_list_entry(node, struct rt_wqueue_node, list);// 获取等待队列控制块 if (entry->wakeup(entry, key) == 0) // wakeup 一定会返回 0 { rt_thread_resume(entry->polling_thread);// 恢复这个线程 need_schedule = 1; //需要调度设置为 1 rt_wqueue_remove(entry); // 从等待队列的链表中移除这个等待队列 break;// 跳出循环遍历 } } } rt_hw_interrupt_enable(level); // 开启中断 if (need_schedule) rt_schedule();// 开启调度 } ``` 4. 默认唤醒函数 ```c int __wqueue_default_wake(struct rt_wqueue_node *wait, void *key) { return 0; } ``` 这里未作任何的操作就进行了返回 `0` ,这里做的太简单粗暴了。 5. 等待队列插入一个节点 ```c void rt_wqueue_add(rt_wqueue_t *queue, struct rt_wqueue_node *node) { rt_base_t level; level = rt_hw_interrupt_disable();// 关中断 rt_list_insert_before(&(queue->waiting_list), &(node->list));// 把 node->list 插入到 queue->waiting_list rt_hw_interrupt_enable(level); // 开中断 } ``` 6. 等待队列移除一个节点 ```c void rt_wqueue_remove(struct rt_wqueue_node *node) { rt_base_t level; level = rt_hw_interrupt_disable(); //关中断 rt_list_remove(&(node->list)); // 移除节点 node rt_hw_interrupt_enable(level); // 开中断 } ``` ### 4. 总结 1. 等待队列的使用了关键字 `rt_inline` 。在 `rt_device` 注册设备时候都使用了 `rt_wqueue_init` ,这里有一个 **C 语言** 的小技巧,可以自己去搜索 `inline` 来学习一下。 2. 进入等待队列的形参 `condition` 必须传递 `0`, 超时时间的单位是 `ms` 3. 唤醒等待队列的条件 `key`,并未实现。如果有需要使用到等待队列的场景不要擅自修改源码,因为 **RTT 设备框架** 大量使用了,擅自修改会导致报错,自己可以参考官方的实现重造一个函数,去实现对 `key` 4. 等待队列的源码位于`/rt_thread_master/components/drivers/src/waitqueue.c` 5. 用户可以自行创建一个 `API` 来实现自定义的 `wake_up`
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
whj467467222
开源,分享,交流,共同进步
文章
32
回答
1222
被采纳
148
关注TA
发私信
相关文章
1
RT-THREAD在STM32H747平台上移植lwip
2
正点原子miniSTM32开发板读写sdcard
3
反馈rtt串口驱动对低功耗串口lpuart1不兼容的问题
4
Keil MDK 移植 RT-Thread Nano
5
RT1061/1052 带 RTT + LWIP和LPSPI,有什么坑要注意吗?
6
RT thread HID 如何收发数据
7
求一份基于RTT系统封装好的STM32F1系列的FLASH操作程序
8
RT-Thread修改项目名称之后不能下载
9
rt-studio编译c++
10
有木有移植rt-thread(nano)到riscv 32位MCU上
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部