历史上的今天
返回首页

历史上的今天

今天是:2025年07月30日(星期三)

正在发生

2021年07月30日 | 基于uCOS-II的任务池/线程池 模块

2021-07-30 来源:eefocus

前言

线程池在软件开发中应用的很广泛,其很适合处理需要并行(并发)处理大量类似任务的情况。比如在web服务器端,需要为不同的socket(用户)同时提供服务,典型的模型就是为每个socket分别分配一个线程,一对一进行服务,这就涉及到大量的线程创建和销毁操作。当然,作为一个嵌入式软件,尤其还是以uCOS为操作系统的,一般是不会拿来作为高性能web服务器的。但是还是有很多时候会需要大量动态的线程。


线程池避免了创建和销毁线程带来的大量开销,使得软件效率得到了极大的提升。


uCOS-II中没有线程这个概念,而是使用了基于优先级的任务,同一时间只会运行所有准备好的任务中优先级最高的那个。但是有的时候又有使用类似线程池功能的需求,于是我基于uCOS的任务写了这个任务池模块,对线程池的功能进行简单地模拟。


模块简介

任务池模型

任务池的模型十分简单,模块内部有一个存放工作任务的消息队列(工作队列),你可以通过TP_WORKQ_IMP来选择工作队列的内部实现。在初始化时会创建TP_TASK_COUNT个任务(工人)来等待工作队列并从中依次提出工作来处理,用户则使用TP_AssignWork函数将每一个工作以及其附件传递给工作队列。


所以那些没法立即处理的工作任务会先缓存在工作队列中,等有工人空闲出来后就会继续处理。工作队列的大小由TP_WORKQ_SIZE决定。


注意,uCOS-II 是一个实时操作系统,而这个模块是基于uCOS-II的任务的。换句话说,只有所有准备好的任务中拥有最高优先级的任务才能运行,这也导致工作任务不保证会按照放入工作队列的顺序来完成。为了把这个池用得像一个真正的线程池:


工作任务最好是那些需要一段时间执行的,典型地比如那些需要与其他驱动、外设或任务交互的工作任务,这些工作任务常需要发出一个指令并等待执行结果。


当等待执行结果时,应该使用能进行任务切换(或说挂起当前任务)的API,比如OSTimeDlyHMSM、OSTimeDly、OSSemPend等。不要使用while(XXX){}来阻塞等待结果。


下面几张图表示出了任务池的整个工作流程(好萌的小人):




很形象吧,感觉不用多解释了。

而且当前实现提供的接口也很简单,就一个初始化以及一个布置任务。


Q&A

Q:为什么要提供那么多工作队列的实现,直接实现一种不就好了?

A:为了帮你省代码呀,比如,如果你在别的地方用到了uCOS-II的消息队列,那选择对应的实现,就相当于把这部分消息队列管理的代码省下来了,只有一点调用api的开销。但如果你专门为了任务池去启用了OS的这个功能,那其实很亏的。所以使用哪一种实现可以根据你项目的其他地方用到了哪个模块。像我个人比较喜欢全部用信号量,所以可能会倾向于选择 基于手写的工作队列结合uCOS-II的信号量 的那个实现。


Q:先布置的工作一定会经过工作队列么?

A:这取决于工作队列的具体实现,如果选择了那两个基于uCOS-II的消息队列的实现的话,如果有工人在pend工作队列,则下一个布置的任务不会放进任务池中,而是会直接交给工人,当然工人会不会立刻去处理还得看优先级的相对关系。而如果选择TP_WORKQ_IMP_QUEUE_OS_SEM这一项的话,则一定会先放进工作队列。


Q:工作任务要像uCOS的任务一样无限循环么?

A:工作任务不应该无限循环,否则工人就表现的像一个普通的uCOS任务一样,而任务池就会丢失一个工人。当然,如果你就是想从任务池中随便取几个任务来当普通任务用,那这符合你的要求。


Q:空闲的工人会立刻把工作队列中已有的工作提走么?

A:由于是基于uCOS-II任务实现的,所以实际上这个问题的答案取决于工人任务与其他已准备好的任务的相对优先级,比如,如果布置工作的任务的优先级低于等待中的工人的,那么它刚刚调用完api布置完任务甚至api还没返回,就会立刻切换到空闲工人的任务,然后工人就会提走工作并执行。而如果布置工作的任务的优先级最高的话,那他连续布置的n个工作都会无法立刻执行,直到其主动放弃cpu时间。


Q:先布置的工作一定会先执行么?

A:不一定,取决于取走工作的工人的相对优先级,极端情况下会出现先布置的任务最后执行的情况,但由于取走工作的顺序也是按照当前空闲工人的优先级的,所以整体上是先布置的先执行。详见上一章的说明。


Q:为什么工作队列的实现选项中还有内存管理这种东西?

A:消息队列只是暂存一个指针,还需要空间来存放相关信息(工作指针及附件),所以需要动态的内存分配。


Q:我用到了大量的uCOS-II内存管理模块,想将这个模块的内存管理与其他的合并?

A:工作队列实现选TP_WORKQ_IMP_OS_Q_PLUS_MEM,然后找到.c文件最后的MEMORY CONTROL IMPLEMENTATION那里进行对应修改。


代码

这里贴出代码:


/*

*********************************************************************************************************

*

*

*                                        Pool of Tasks for uCOS-II

*                                             uCOS-II 任务池

*

* File : TaskPool.h

* By   : Lin Shijun(http://blog.csdn.net/lin_strong)

* Date : 2018/08/08

* version: V1.0

* NOTE(s): This module implement a pool of tasks based on uCOS-II.

*          There is a message queues for works(workqueue) internal(choose the implementation by TP_WORKQ_IMP).

*        the module will initial TP_TASK_COUNT tasks(workers) to pend workqueue and pick the work one by one to 

*        process, and user call TP_AssignWork to pass work with its attachment to the workqueue.

*          So the works that can't be processed immediately will be buffered in the workqueue wating for 

*        processing.The size of the workqueue is defined by TP_WORKQ_SIZE. 

*          Note that, uCOS-II is a real-time OS, and this module is based on uCOS-II's tasks, which means

*        only the task which has highest priority among all ready tasks will get cpu time to run, and works

*        may not be processed as the order be put in the workqueue. To use the pool like a real "thread pool":

*          1. the works had better take a while to process, typically the work that interacts with other

*        drivers, peripherals or tasks, and need time wating for result of command;

*          2. the works should use APIs that enable task switching when wating for result of command, e.g

*        OSTimeDlyHMSM, OSTimeDly, OSSemPend and so on. Don't use while(XXX){} to pend result.

*          这个模块实现了基于uCOS-II的任务池。

*          模块内部有一个存放工作任务的消息队列(工作队列),你可以通过TP_WORKQ_IMP来选择工作队列的内部实现。

*        在初始化时会创建TP_TASK_COUNT个任务(工人)来等待工作队列并从中依次提出工作来处理,用户则使用

*        TP_AssignWork函数将每一个工作以及其附件传递给工作队列。

*          所以那些没法立即处理的工作任务会先缓存在工作队列中,等有工人空闲出来后就会继续处理。工作队列的

*        大小由TP_WORKQ_SIZE决定。

*          注意,uCOS-II 是一个实时操作系统,而这个模块是基于uCOS-II的任务的。换句话说,只有所有准备好的任务中

*        拥有最高优先级的任务才能运行,这也导致工作任务不保证会按照放入工作队列的顺序来完成。为了把这个池用得

*        像一个真正的线程池:

*          1. 工作任务最好是那些需要一段时间执行的,典型地比如那些需要与其他驱动、外设或任务交互的工作任务,

*        这些工作任务常需要发出一个指令并等待执行结果。

*          2. 当等待执行结果时,应该使用能进行任务切换(或说挂起当前任务)的API,比如OSTimeDlyHMSM、OSTimeDly、

*        OSSemPend等。不要使用while(XXX){}来阻塞等待结果。

*          

*          the work shouldn't has infinite loop, or the worker will act like a normol uCOS-II task, and the

*        task pool will lose the worker.

*          工作任务不应该无限循环,否则工人就表现的像一个普通的uCOS任务一样,而任务池就会丢失一个工人。

* History : 2018/08/08   the original version of TaskPool.

*********************************************************************************************************

*/



#ifndef   TASK_POOL_H

#define   TASK_POOL_H

/*

*********************************************************************************************************

*                                       INCLUDE

*********************************************************************************************************

*/

// based on ucos_ii

#include "ucos_ii.h"


/*

*********************************************************************************************************

*                                       MISCELLANEOUS

*********************************************************************************************************

*/


#ifndef  FALSE

#define  FALSE    0

#endif


#ifndef  TRUE

#define  TRUE     1

#endif


/*

*********************************************************************************************************

*                                        CONSTANTS     常量

*********************************************************************************************************

*/

#define TP_ERR_NONE                        0u


#define TP_ERR_POINTER_NULL                1u

#define TP_ERR_TASK_INIT                   2u

#define TP_ERR_WORKQ_INIT                  3u

//#define TP_ERR_MEM_INIT                    4u


#define TP_ERR_WORKQ_FULL                  5u

#define TP_ERR_MEMORY                      6u


#define TP_ERR_UNKNOWN                     254u


// choose the implementation of internal workqueue 可选的工作队列内部实现方式

#define TP_WORKQ_IMP_OS_Q_PLUS_MEM   1   // based on uCOS-II's Message Queue and memory management

                                         // 基于uCOS-II的消息队列和内存管理

#define TP_WORKQ_IMP_OS_Q_STD_MEM    2   // based on uCOS-II's Message Queue and stdlib's memory management

                                         // 基于uCOS-II的消息队列和标准库的内存管理

#define TP_WORKQ_IMP_QUEUE_OS_SEM    3   // based on manually queue and uCOS-II's semaphore

                                         // 基于手写的工作队列结合uCOS-II的信号量

/*

*********************************************************************************************************

*                                       CONFIGURATION  配置

*********************************************************************************************************

*/


// NOTE: the init procedure will create tasks, whose priority from TP_TASK_PRIO_START to 

//      (TP_TASK_PRIO_START + TP_TASK_COUNT - 1), and let them work in the pool

// 注意:在初始化时,初始化函数会创建优先级从TP_TASK_PRIO_START开始的TP_TASK_COUNT个任务放入任务池,

//       用户需保证没有优先级冲突

#define TP_TASK_PRIO_START      20       // the priority of the first task in the pool

                                         // 任务池中第一任务的优先级

#define TP_TASK_COUNT           4        // the number of tasks in the pool           

                                         // 任务池中任务数

#define TP_TASK_STACK_SIZE      300      // the size of stacks of each tasks in the pool 

推荐阅读

史海拾趣

富士康(FOXCONN)公司的发展小趣事

为了更好地服务全球客户,FMS积极实施全球化战略。公司在中国深圳设立了子公司——深圳市美丽微半导体有限公司,作为在中国大陆的主要生产基地与销售窗口。同时,FMS还在亚洲、欧洲、美洲等地设立了多个销售据点,形成了覆盖全球的营销网络。这一布局不仅提升了FMS的市场响应速度,也极大地增强了其品牌影响力。

AURORA公司的发展小趣事

AURORA公司一直致力于提升自动驾驶技术的安全性和可靠性。通过不断研发和创新,AURORA在自动驾驶算法、传感器融合、数据处理等方面取得了显著进展。公司的技术团队不断攻克技术难题,推动自动驾驶技术向更高等级迈进。同时,AURORA还积极与高校和研究机构合作,共同推动自动驾驶技术的研发和应用。

Dicon Fiberoptics Inc公司的发展小趣事

为了进一步扩大市场份额和提升品牌影响力,DiCon积极开展国际合作与全球布局。公司与多家国际知名企业建立了战略合作关系,共同研发和推广光纤通信技术。同时,DiCon还在全球范围内设立了多个研发中心和销售分支机构,以便更好地服务当地客户和市场。

BCD Semi(Diodes)公司的发展小趣事

在电子行业的发展过程中,市场波动和风险挑战是不可避免的。某一年,全球半导体市场出现了严重的产能过剩,导致产品价格大幅下跌。面对这一挑战,BCD Semi(Diodes)公司及时调整生产策略,优化产品结构,降低生产成本,成功度过了这一行业寒冬。

Allied Wire & Cable Inc公司的发展小趣事

随着公司业务的不断发展,Allied公司开始积极拓展产品线,逐渐涵盖了多种类型的电线电缆产品,满足了不同客户的需求。同时,公司还积极开拓新的市场领域,将产品推广至全国范围,并逐渐拓展至国际市场。通过不断的产品创新和市场拓展,Allied公司的业务规模逐渐扩大,市场份额也稳步提升。

Concurrent Logic公司的发展小趣事

Concurrent Logic公司的创立源于一群工程师对并行计算和逻辑设计的热爱。在公司成立初期,他们面临着巨大的挑战。由于资金有限,他们不得不在狭小的办公室内开始他们的创新之旅。初创团队的成员们夜以继日地工作,开发并优化他们的第一个产品——一款高性能的并行处理芯片。尽管条件艰苦,但他们的努力和热情为公司的未来奠定了坚实的基础。

问答坊 | AI 解惑

数据结构算法与应用——错过后,你也许找不到更好的了

这是一本很不错的电子书,希望对您有所帮助 顺便赚点钱,嘿嘿......…

查看全部问答>

传输线和反射的经典文章

传输线和反射的经典文章…

查看全部问答>

请教:WINCE里如何预设WIFI IP和WIFI的相关登录设置?

比如要预设IP、网关、DNS, 还有WIFI的加密方式、密码。 我想应该是修改注册表,但是我不知道在哪改,请指教。 全部的总数奉上!…

查看全部问答>

有谁能提供USB鼠标键盘驱动,模拟鼠标键盘自动按键,以前用了wion.vxd,但是USB不可以

有谁能提供USB鼠标键盘驱动,模拟鼠标键盘自动按键,以前用了wion.vxd驱动,PS/2接口可以模拟按键盘, 但是USB接口不可以模拟按键 如果人能提供这样的驱动程序,本人不胜感激,大大给分,给分... …

查看全部问答>

请教windows mobile 中文开发环境搭建步骤

如题,我用的是vs 2005, 请教高手们如何搭建中文的 mobile开发环境,多谢指教。…

查看全部问答>

求一个可以 load nk.bin 的 loadcepc.exe 的可执行文件, 在线等!!!

这个文件编译太费事了, 找了个 msvc152 的编译器, 一堆编译错误。 有那位兄弟有  loadcepc.exe 的可执行文件, 最普通的就行了,我想在 cepc 上试一个东西。 mail: bamu1984 # 163.com …

查看全部问答>

新手帖 单片机控制CPLD读取SRAM

library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity read_write is port( clk : in std_logic ;       mcu_oe    : in    std_logic;        ...…

查看全部问答>

电磁场屏蔽的机理 续

与前面已讲述的电场屏蔽及磁场屏蔽的机理不同,电磁屏 蔽对于电磁波的衰减有三种不同的机理: x 当电磁波在到达屏蔽体表面时,由于空气与金属的交界面上 阻抗的不连续,对入射波产生的反射。这种反射不要求屏蔽 材料必须有一定厚度,只要求交界 ...…

查看全部问答>

Linux与FreeBSD系统的十个本质区别

http://www.bsechr.com.cn/news.asp?anclassid=58&mnclassid=165Linux的标志是一只十分可爱的小企鹅,而FreeBSD的标志是一个拿着叉子的小恶魔。你是否经常会听到人们把 Linux及 BSD 系统混为一谈?是的,我有时会经常听到一些新手,甚至于媒体都这 ...…

查看全部问答>