RT-Thread 隐藏的宝藏之等待队列

9 篇文章 8 订阅
8 篇文章 0 订阅

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 : 需要等待的时间,单位是毫秒

  1. 唤醒等待队列

    void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key)

    queue : 等待队列的控制块

    key : 唤醒条件,目前源码中未使用

  2. 在等待队列中插入一个节点

    void rt_wqueue_add(rt_wqueue_t *queue, struct rt_wqueue_node *node)

    queue : 等待队列控制块

    node : 插入队列的节点

  3. 从等待队列移除一个节点

    void rt_wqueue_remove(struct rt_wqueue_node *node)

    node : 需要移除的节点

3. 等待队列的实现

  1. 初始化等待队列

    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. 加入等待队列

    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. 唤醒等待队列

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();// 开启调度
}
  1. 默认唤醒函数
int __wqueue_default_wake(struct rt_wqueue_node *wait, void *key)
{
    return 0;
}

这里未作任何的操作就进行了返回 0 ,这里做的太简单粗暴了。

  1. 等待队列插入一个节点
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); // 开中断
}
  1. 等待队列移除一个节点
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
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值