历史上的今天
今天是: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
史海拾趣
|
请教:WINCE里如何预设WIFI IP和WIFI的相关登录设置? 比如要预设IP、网关、DNS, 还有WIFI的加密方式、密码。 我想应该是修改注册表,但是我不知道在哪改,请指教。 全部的总数奉上!… 查看全部问答> |
|
有谁能提供USB鼠标键盘驱动,模拟鼠标键盘自动按键,以前用了wion.vxd,但是USB不可以 有谁能提供USB鼠标键盘驱动,模拟鼠标键盘自动按键,以前用了wion.vxd驱动,PS/2接口可以模拟按键盘, 但是USB接口不可以模拟按键 如果人能提供这样的驱动程序,本人不胜感激,大大给分,给分... … 查看全部问答> |
|
求一个可以 load nk.bin 的 loadcepc.exe 的可执行文件, 在线等!!! 这个文件编译太费事了, 找了个 msvc152 的编译器, 一堆编译错误。 有那位兄弟有 loadcepc.exe 的可执行文件, 最普通的就行了,我想在 cepc 上试一个东西。 mail: bamu1984 # 163.com … 查看全部问答> |
|
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 当电磁波在到达屏蔽体表面时,由于空气与金属的交界面上 阻抗的不连续,对入射波产生的反射。这种反射不要求屏蔽 材料必须有一定厚度,只要求交界 ...… 查看全部问答> |
|
http://www.bsechr.com.cn/news.asp?anclassid=58&mnclassid=165Linux的标志是一只十分可爱的小企鹅,而FreeBSD的标志是一个拿着叉子的小恶魔。你是否经常会听到人们把 Linux及 BSD 系统混为一谈?是的,我有时会经常听到一些新手,甚至于媒体都这 ...… 查看全部问答> |




