[嵌入式开发模块]基于uCOS-II的任务池/线程池 模块

Myth丶恋晨 2022-05-16 07:22 260阅读 0赞

前言

线程池在软件开发中应用的很广泛,其很适合处理需要并行(并发)处理大量类似任务的情况。比如在web服务器端,需要为不同的socket(用户)同时提供服务,典型的模型就是为每个socket分别分配一个线程,一对一进行服务,这就涉及到大量的线程创建和销毁操作。当然,作为一个嵌入式软件,尤其还是以uCOS为操作系统的,一般是不会拿来作为高性能web服务器的。但是还是有很多时候会需要大量动态的线程。
线程池避免了创建和销毁线程带来的大量开销,使得软件效率得到了极大的提升。
uCOS-II中没有线程这个概念,而是使用了基于优先级的任务,同一时间只会运行所有准备好的任务中优先级最高的那个。但是有的时候又有使用类似线程池功能的需求,于是我基于uCOS的任务写了这个任务池模块,对线程池的功能进行简单地模拟。

模块简介

任务池模型

任务池的模型十分简单,模块内部有一个存放工作任务的消息队列(工作队列),你可以通过TP_WORKQ_IMP来选择工作队列的内部实现。在初始化时会创建TP_TASK_COUNT个任务(工人)来等待工作队列并从中依次提出工作来处理,用户则使用TP_AssignWork函数将每一个工作以及其附件传递给工作队列。
所以那些没法立即处理的工作任务会先缓存在工作队列中,等有工人空闲出来后就会继续处理。工作队列的大小由TP_WORKQ_SIZE决定。
注意,uCOS-II 是一个实时操作系统,而这个模块是基于uCOS-II的任务的。换句话说,只有所有准备好的任务中拥有最高优先级的任务才能运行,这也导致工作任务不保证会按照放入工作队列的顺序来完成。为了把这个池用得像一个真正的线程池:

  1. 工作任务最好是那些需要一段时间执行的,典型地比如那些需要与其他驱动、外设或任务交互的工作任务,这些工作任务常需要发出一个指令并等待执行结果。
  2. 当等待执行结果时,应该使用能进行任务切换(或说挂起当前任务)的API,比如OSTimeDlyHMSM、OSTimeDly、OSSemPend等。不要使用while(XXX){}来阻塞等待结果。

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

很形象吧,感觉不用多解释了。
而且当前实现提供的接口也很简单,就一个初始化以及一个布置任务。

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那里进行对应修改。

代码

这里贴出代码:

  1. /*
  2. *********************************************************************************************************
  3. *
  4. *
  5. * Pool of Tasks for uCOS-II
  6. * uCOS-II 任务池
  7. *
  8. * File : TaskPool.h
  9. * By : Lin Shijun(http://blog.csdn.net/lin_strong)
  10. * Date : 2018/08/08
  11. * version: V1.0
  12. * NOTE(s): This module implement a pool of tasks based on uCOS-II.
  13. * There is a message queues for works(workqueue) internal(choose the implementation by TP_WORKQ_IMP).
  14. * the module will initial TP_TASK_COUNT tasks(workers) to pend workqueue and pick the work one by one to
  15. * process, and user call TP_AssignWork to pass work with its attachment to the workqueue.
  16. * So the works that can't be processed immediately will be buffered in the workqueue wating for
  17. * processing.The size of the workqueue is defined by TP_WORKQ_SIZE.
  18. * Note that, uCOS-II is a real-time OS, and this module is based on uCOS-II's tasks, which means
  19. * only the task which has highest priority among all ready tasks will get cpu time to run, and works
  20. * may not be processed as the order be put in the workqueue. To use the pool like a real "thread pool":
  21. * 1. the works had better take a while to process, typically the work that interacts with other
  22. * drivers, peripherals or tasks, and need time wating for result of command;
  23. * 2. the works should use APIs that enable task switching when wating for result of command, e.g
  24. * OSTimeDlyHMSM, OSTimeDly, OSSemPend and so on. Don't use while(XXX){} to pend result.
  25. * 这个模块实现了基于uCOS-II的任务池。
  26. * 模块内部有一个存放工作任务的消息队列(工作队列),你可以通过TP_WORKQ_IMP来选择工作队列的内部实现。
  27. * 在初始化时会创建TP_TASK_COUNT个任务(工人)来等待工作队列并从中依次提出工作来处理,用户则使用
  28. * TP_AssignWork函数将每一个工作以及其附件传递给工作队列。
  29. * 所以那些没法立即处理的工作任务会先缓存在工作队列中,等有工人空闲出来后就会继续处理。工作队列的
  30. * 大小由TP_WORKQ_SIZE决定。
  31. * 注意,uCOS-II 是一个实时操作系统,而这个模块是基于uCOS-II的任务的。换句话说,只有所有准备好的任务中
  32. * 拥有最高优先级的任务才能运行,这也导致工作任务不保证会按照放入工作队列的顺序来完成。为了把这个池用得
  33. * 像一个真正的线程池:
  34. * 1. 工作任务最好是那些需要一段时间执行的,典型地比如那些需要与其他驱动、外设或任务交互的工作任务,
  35. * 这些工作任务常需要发出一个指令并等待执行结果。
  36. * 2. 当等待执行结果时,应该使用能进行任务切换(或说挂起当前任务)的API,比如OSTimeDlyHMSM、OSTimeDly、
  37. * OSSemPend等。不要使用while(XXX){}来阻塞等待结果。
  38. *
  39. * the work shouldn't has infinite loop, or the worker will act like a normol uCOS-II task, and the
  40. * task pool will lose the worker.
  41. * 工作任务不应该无限循环,否则工人就表现的像一个普通的uCOS任务一样,而任务池就会丢失一个工人。
  42. * History : 2018/08/08 the original version of TaskPool.
  43. *********************************************************************************************************
  44. */
  45. #ifndef TASK_POOL_H
  46. #define TASK_POOL_H
  47. /*
  48. *********************************************************************************************************
  49. * INCLUDE
  50. *********************************************************************************************************
  51. */
  52. // based on ucos_ii
  53. #include "ucos_ii.h"
  54. /*
  55. *********************************************************************************************************
  56. * MISCELLANEOUS
  57. *********************************************************************************************************
  58. */
  59. #ifndef FALSE
  60. #define FALSE 0
  61. #endif
  62. #ifndef TRUE
  63. #define TRUE 1
  64. #endif
  65. /*
  66. *********************************************************************************************************
  67. * CONSTANTS 常量
  68. *********************************************************************************************************
  69. */
  70. #define TP_ERR_NONE 0u
  71. #define TP_ERR_POINTER_NULL 1u
  72. #define TP_ERR_TASK_INIT 2u
  73. #define TP_ERR_WORKQ_INIT 3u
  74. //#define TP_ERR_MEM_INIT 4u
  75. #define TP_ERR_WORKQ_FULL 5u
  76. #define TP_ERR_MEMORY 6u
  77. #define TP_ERR_UNKNOWN 254u
  78. // choose the implementation of internal workqueue 可选的工作队列内部实现方式
  79. #define TP_WORKQ_IMP_OS_Q_PLUS_MEM 1 // based on uCOS-II's Message Queue and memory management
  80. // 基于uCOS-II的消息队列和内存管理
  81. #define TP_WORKQ_IMP_OS_Q_STD_MEM 2 // based on uCOS-II's Message Queue and stdlib's memory management
  82. // 基于uCOS-II的消息队列和标准库的内存管理
  83. #define TP_WORKQ_IMP_QUEUE_OS_SEM 3 // based on manually queue and uCOS-II's semaphore
  84. // 基于手写的工作队列结合uCOS-II的信号量
  85. /*
  86. *********************************************************************************************************
  87. * CONFIGURATION 配置
  88. *********************************************************************************************************
  89. */
  90. // NOTE: the init procedure will create tasks, whose priority from TP_TASK_PRIO_START to
  91. // (TP_TASK_PRIO_START + TP_TASK_COUNT - 1), and let them work in the pool
  92. // 注意:在初始化时,初始化函数会创建优先级从TP_TASK_PRIO_START开始的TP_TASK_COUNT个任务放入任务池,
  93. // 用户需保证没有优先级冲突
  94. #define TP_TASK_PRIO_START 20 // the priority of the first task in the pool
  95. // 任务池中第一任务的优先级
  96. #define TP_TASK_COUNT 4 // the number of tasks in the pool
  97. // 任务池中任务数
  98. #define TP_TASK_STACK_SIZE 300 // the size of stacks of each tasks in the pool
  99. // 任务池中每个任务的栈大小
  100. #define TP_WORKQ_SIZE 5 // the size of the work-queue (if there is no task available,the
  101. // new work will be put in the work-queue wating to be process).
  102. // 工作队列的大小(如果没有空闲任务,新的工作将放入工作队列中
  103. // 等待被提走处理)
  104. #define TP_ARGUMENT_CHECK_EN TRUE // TRUE: arguments will be checked, however,this will
  105. // cost a little code volume.
  106. // choose the implementation of internal workqueue(SEE TP_WORKQ_IMP_XXXXX)
  107. // 选择工作队列的内部实现方式
  108. #define TP_WORKQ_IMP TP_WORKQ_IMP_QUEUE_OS_SEM
  109. // for OS_MEM implementation of memory control(TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_PLUS_MEM)
  110. // reserve how many space for work-items for dynamic allocation.
  111. // 对于使用OS_MEM来实现内存控制这种情况时,要预留多少个工作项的空间来动态分配
  112. #define TP_WORKM_SIZE (TP_WORKQ_SIZE + TP_TASK_COUNT)
  113. /*
  114. *********************************************************************************************************
  115. * FUNCTION PROTOTYPES 函数原型
  116. *********************************************************************************************************
  117. */
  118. INT8U TP_Init(void);
  119. INT8U TP_AssignWork(void (*work)(void *att),void *att);
  120. /*
  121. *********************************************************************************************************
  122. * ERROR CHECK
  123. *********************************************************************************************************
  124. */
  125. #if(TP_WORKQ_IMP != TP_WORKQ_IMP_OS_Q_PLUS_MEM && \
  126. TP_WORKQ_IMP != TP_WORKQ_IMP_OS_Q_STD_MEM && \
  127. TP_WORKQ_IMP != TP_WORKQ_IMP_QUEUE_OS_SEM)
  128. #error choose one implementation of internal workqueue(TP_WORKQ_IMP)
  129. #endif
  130. #endif
  131. /*
  132. *********************************************************************************************************
  133. *
  134. *
  135. * Pool of Tasks for uCOS-II
  136. * uCOS-II 任务池
  137. *
  138. * File : TaskPool.c
  139. * By : Lin Shijun(http://blog.csdn.net/lin_strong)
  140. * Date : 2018/08/08
  141. * version: V1.0
  142. * NOTE(s): This module implement a pool of tasks based on uCOS-II.
  143. * History : 2018/08/08 the original version of TaskPool.
  144. *********************************************************************************************************
  145. */
  146. /*
  147. *********************************************************************************************************
  148. * INCLUDES
  149. *********************************************************************************************************
  150. */
  151. #include "TaskPool.h"
  152. #include <stddef.h>
  153. #if(TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_STD_MEM)
  154. #include <stdlib.h>
  155. #endif
  156. /*
  157. *********************************************************************************************************
  158. * DATA TYPE 数据类型
  159. *********************************************************************************************************
  160. */
  161. typedef struct work_item{
  162. void (*work)(void *att); // the work to do
  163. void *att; // attachment of the work
  164. } WORK_ITEM,* pWORK_ITEM;
  165. /*
  166. *********************************************************************************************************
  167. * LOCAL FUNCTION DECLARATION
  168. *********************************************************************************************************
  169. */
  170. #if(TP_ARGUMENT_CHECK_EN == TRUE)
  171. #define argCheck(cond,rVal) if(cond) {return (rVal); }
  172. #else
  173. #define argCheck(cond,rVal)
  174. #endif // of (TP_ARGUMENT_CHECK_EN == TRUE)
  175. static void _WorkerTask(void *p_arg);
  176. // work-queue
  177. // return: TRUE success
  178. // FALSE any error
  179. static INT8U _WorkQueue_Init(void);
  180. // return: TRUE success
  181. // FALSE any error
  182. static INT8U _WorkQueue_Out(pWORK_ITEM rst);
  183. static INT8U _WorkQueue_In(void (*work)(void *att),void *att);
  184. /*
  185. *********************************************************************************************************
  186. * LOCAL VARIABLE
  187. *********************************************************************************************************
  188. */
  189. // workers's task
  190. static INT8U WorkersStk[TP_TASK_STACK_SIZE * TP_TASK_COUNT];
  191. /*
  192. *********************************************************************************************************
  193. * Task Pool initialization
  194. *
  195. * Description : To initialize the task pool. 初始化任务池
  196. *
  197. * Arguments :
  198. *
  199. * Return : TP_ERR_NONE if success 成功
  200. * TP_ERR_TASK_INIT if err when initialize tasks 初始化任务时发生问题
  201. * TP_ERR_WORKQ_INIT if err when initialize work queue 初始化工作队列时出现问题
  202. *Note(s):
  203. *********************************************************************************************************
  204. */
  205. INT8U TP_Init(void){
  206. INT8U i,err;
  207. // create tasks for all workers
  208. for(i = 0;i < TP_TASK_COUNT; i++){
  209. #if (OS_TASK_CREATE_EXT_EN > 0)
  210. err = OSTaskCreateExt(_WorkerTask,
  211. NULL,
  212. (OS_STK *)&WorkersStk[TP_TASK_STACK_SIZE * (i + 1) - 1],
  213. TP_TASK_PRIO_START + i,
  214. TP_TASK_PRIO_START + i,
  215. (OS_STK *)&WorkersStk[TP_TASK_STACK_SIZE * i],
  216. TP_TASK_STACK_SIZE,
  217. NULL,
  218. OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
  219. #else
  220. err = OSTaskCreate(_WorkerTask,
  221. NULL,
  222. (OS_STK *)&WorkersStk[TP_TASK_STACK_SIZE * (i + 1) - 1],
  223. TP_TASK_PRIO_START + i);
  224. #endif
  225. if(err != OS_ERR_NONE)
  226. return TP_ERR_TASK_INIT;
  227. }
  228. if(!_WorkQueue_Init())
  229. return TP_ERR_WORKQ_INIT;
  230. return TP_ERR_NONE;
  231. }
  232. /*
  233. *********************************************************************************************************
  234. * Assign a work to Task Pool
  235. *
  236. * Description : To assign a work to the task pool. 指派工作给任务池
  237. *
  238. * Arguments : work the work to do 要做的工作
  239. * att attachment of the work(pass some argument to the work) 工作的附件
  240. *
  241. * Return : TP_ERR_NONE if success 成功
  242. * TP_ERR_POINTER_NULL if work == NULL 空指针
  243. * TP_ERR_MEMORY if out of memory 内存不够用
  244. * TP_ERR_WORKQ_FULL if work queue is full 工作队列满
  245. * TP_ERR_UNKNOWN if fatal error 未知错误
  246. * Note(s):
  247. *********************************************************************************************************
  248. */
  249. INT8U TP_AssignWork(void (*work)(void *att),void *att){
  250. argCheck(work == NULL, TP_ERR_POINTER_NULL);
  251. return _WorkQueue_In(work,att);
  252. }
  253. /*
  254. *********************************************************************************************************
  255. * Worker's Task
  256. *
  257. * Description : Task for workers. 工人们的Task
  258. *
  259. * Arguments :
  260. *
  261. * Return :
  262. *
  263. * Note(s) :
  264. *********************************************************************************************************
  265. */
  266. // 当前工人的任务就是无限取出工作并执行
  267. static void _WorkerTask(void *p_arg){
  268. WORK_ITEM item;
  269. while(TRUE){
  270. if(_WorkQueue_Out(&item)){ // get next work and
  271. item.work(item.att); // do the work
  272. }
  273. }
  274. }
  275. /*
  276. *********************************************************************************************************
  277. * WORKQUEUE IMPLEMENTATION
  278. *********************************************************************************************************
  279. */
  280. #if(TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_PLUS_MEM || TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_STD_MEM)
  281. // memory control
  282. // return: TRUE success
  283. // FALSE any error
  284. static INT8U _MemctrlInit(void);
  285. // return NULL if fail
  286. static pWORK_ITEM _WorkItem_Create(void);
  287. static void _WorkItem_Dispose(pWORK_ITEM p);
  288. static OS_EVENT *WorkQueue;
  289. static pWORK_ITEM WorkPtrs[TP_WORKQ_SIZE];
  290. //static volatile OS_Q *p; 调试时看队列用
  291. static INT8U _WorkQueue_Init(void){
  292. WorkQueue = OSQCreate(WorkPtrs,TP_WORKQ_SIZE);
  293. //p = WorkQueue->OSEventPtr;
  294. return (WorkQueue != NULL) && _MemctrlInit();
  295. }
  296. static INT8U _WorkQueue_Out(pWORK_ITEM rst){
  297. INT8U err;
  298. pWORK_ITEM pItem;
  299. // get next work
  300. pItem = (pWORK_ITEM)OSQPend(WorkQueue,0,&err);
  301. if(err != OS_ERR_NONE)
  302. return FALSE;
  303. // store the message and return the memory
  304. *rst = *pItem;
  305. _WorkItem_Dispose(pItem);
  306. return TRUE;
  307. }
  308. static INT8U _WorkQueue_In(void (*work)(void *att),void *att){
  309. INT8U err;
  310. pWORK_ITEM pItem;
  311. // get a workitem and store the message
  312. pItem = _WorkItem_Create();
  313. if(pItem == NULL)
  314. return TP_ERR_MEMORY;
  315. pItem->work = work;
  316. pItem->att = att;
  317. err = OSQPost(WorkQueue,pItem);
  318. if(err == OS_ERR_NONE)
  319. return TP_ERR_NONE;
  320. _WorkItem_Dispose(pItem);
  321. if(err == OS_ERR_Q_FULL)
  322. return TP_ERR_WORKQ_FULL;
  323. return TP_ERR_UNKNOWN;
  324. }
  325. #endif
  326. #if(TP_WORKQ_IMP == TP_WORKQ_IMP_QUEUE_OS_SEM)
  327. typedef struct {
  328. WORK_ITEM Items[TP_WORKQ_SIZE];
  329. #if(TP_WORKQ_SIZE > 255)
  330. INT16U pIn; // index of the next item to be put.
  331. INT16U pOut; // index of the next item to be picked out.
  332. INT16U cnt;
  333. #else
  334. INT8U pIn;
  335. INT8U pOut;
  336. INT8U cnt;
  337. #endif
  338. }WORK_QUEUE;
  339. static WORK_QUEUE WorkQueue;
  340. static OS_EVENT *WorkQueueSem;
  341. static INT8U _WorkQueue_Init(void){
  342. WorkQueueSem = OSSemCreate(0);
  343. return WorkQueueSem != NULL;
  344. }
  345. static INT8U _WorkQueue_Out(pWORK_ITEM rst){
  346. INT8U err;
  347. #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
  348. OS_CPU_SR cpu_sr = 0;
  349. #endif
  350. // get next work
  351. OSSemPend(WorkQueueSem,0,&err);
  352. if(err != OS_ERR_NONE)
  353. return FALSE;
  354. OS_ENTER_CRITICAL();
  355. if(WorkQueue.cnt == 0) { // conflict with another task
  356. OS_EXIT_CRITICAL();
  357. return FALSE;
  358. }
  359. WorkQueue.cnt--;
  360. *rst = WorkQueue.Items[WorkQueue.pOut];
  361. if(++WorkQueue.pOut >= TP_WORKQ_SIZE)
  362. WorkQueue.pOut = 0;
  363. OS_EXIT_CRITICAL();
  364. return TRUE;
  365. }
  366. static INT8U _WorkQueue_In(void (*work)(void *att),void *att){
  367. INT8U err;
  368. #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
  369. OS_CPU_SR cpu_sr = 0;
  370. #endif
  371. OS_ENTER_CRITICAL();
  372. if(WorkQueue.cnt >= TP_WORKQ_SIZE) { // queue full
  373. OS_EXIT_CRITICAL();
  374. return TP_ERR_WORKQ_FULL;
  375. }
  376. WorkQueue.cnt++;
  377. WorkQueue.Items[WorkQueue.pIn].work = work;
  378. WorkQueue.Items[WorkQueue.pIn].att = att;
  379. if(++WorkQueue.pIn >= TP_WORKQ_SIZE)
  380. WorkQueue.pIn = 0;
  381. OS_EXIT_CRITICAL();
  382. // post new work
  383. err = OSSemPost(WorkQueueSem);
  384. if(err == OS_ERR_NONE)
  385. return TP_ERR_NONE;
  386. else
  387. return TP_ERR_UNKNOWN;
  388. }
  389. #endif
  390. /*
  391. *********************************************************************************************************
  392. * MEMORY CONTROL IMPLEMENTATION
  393. *********************************************************************************************************
  394. */
  395. // memory control by OS_MEM
  396. #if(TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_PLUS_MEM)
  397. static WORK_ITEM WorkEntities[TP_WORKM_SIZE];
  398. static OS_MEM *WorkPool;
  399. static INT8U _MemctrlInit(void){
  400. INT8U err;
  401. WorkPool = OSMemCreate(&WorkEntities[0],TP_WORKM_SIZE,sizeof(WORK_ITEM),&err);
  402. return WorkPool != NULL;
  403. }
  404. static pWORK_ITEM _WorkItem_Create(void){
  405. INT8U err;
  406. pWORK_ITEM rst;
  407. rst = (pWORK_ITEM)OSMemGet(WorkPool,&err);
  408. if(err == OS_ERR_NONE)
  409. return rst;
  410. else
  411. return NULL;
  412. }
  413. static void _WorkItem_Dispose(pWORK_ITEM p){
  414. OSMemPut(WorkPool,(void *)p);
  415. }
  416. #endif
  417. // memory control by C standard library
  418. #if(TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_STD_MEM)
  419. static INT8U _MemctrlInit(void){
  420. return TRUE;
  421. }
  422. static pWORK_ITEM _WorkItem_Create(void){
  423. return (pWORK_ITEM)malloc(sizeof(WORK_ITEM));
  424. }
  425. static void _WorkItem_Dispose(pWORK_ITEM p){
  426. free((void *)p);
  427. }
  428. #endif

示例程序

示例程序就简单搞两个输出信息的工作。
至于sprintf前为什么要加个OSSchedLock()呢。
见我的上一篇博文:https://blog.csdn.net/lin_strong/article/details/81505396

  1. /*
  2. *********************************************************************************************************
  3. * Example Code for TaskPool
  4. *
  5. * By : Lin Shijun
  6. *********************************************************************************************************
  7. */
  8. #include "includes.h"
  9. #include "SCI_def.h"
  10. #include <string.h>
  11. #include "TaskPool.h"
  12. #include <stdio.h>
  13. /*
  14. *********************************************************************************************************
  15. * STACK SPACE DECLARATION
  16. *********************************************************************************************************
  17. */
  18. static OS_STK AppTaskStartStk[APP_TASK_START_STK_SIZE];
  19. /*
  20. *********************************************************************************************************
  21. * TASK FUNCTION DECLARATION
  22. *********************************************************************************************************
  23. */
  24. static void AppTaskStart(void *p_arg);
  25. /*
  26. *********************************************************************************************************
  27. * LOCAL FUNCTION DECLARE
  28. *********************************************************************************************************
  29. */
  30. // SCI工作
  31. static void SCIOutPut1(void *msg);
  32. static void SCIOutPut2(void *msg);
  33. /*
  34. *********************************************************************************************************
  35. * MAIN FUNCTION
  36. *********************************************************************************************************
  37. */
  38. void main(void) {
  39. INT8U err;
  40. BSP_IntDisAll(); /* Disable ALL interrupts to the interrupt controller */
  41. OSInit(); /* Initialize uC/OS-II */
  42. err = OSTaskCreate(AppTaskStart,
  43. NULL,
  44. (OS_STK *)&AppTaskStartStk[APP_TASK_START_STK_SIZE - 1],
  45. APP_TASK_START_PRIO);
  46. OSStart();
  47. }
  48. static void assignWork(void (*work)(void *msg),const char *str){
  49. switch(TP_AssignWork(work,str)){
  50. case TP_ERR_NONE:
  51. break;
  52. case TP_ERR_POINTER_NULL:
  53. SCI_PutCharsB_Mutex(SCI0,"pointer null\r",13,0);
  54. break;
  55. case TP_ERR_MEMORY:
  56. SCI_PutCharsB_Mutex(SCI0,"out of memory\r",14,0);
  57. break;
  58. case TP_ERR_WORKQ_FULL:
  59. SCI_PutCharsB_Mutex(SCI0,"wq full\r",8,0);
  60. break;
  61. case TP_ERR_UNKNOWN:
  62. default:
  63. SCI_PutCharsB_Mutex(SCI0,"fatal err\r",10,0);
  64. break;
  65. }//*/
  66. }
  67. static void AppTaskStart (void *p_arg){
  68. INT8U err;
  69. (void)p_arg; /* Prevent compiler warning */
  70. BSP_Init();
  71. SCI_Init(SCI0);
  72. SCI_EnableTrans(SCI0);
  73. SCI_EnableRxInt(SCI0);
  74. SCI_EnableRecv(SCI0);
  75. SCI_BufferInit();
  76. TP_Init();
  77. OSTimeDlyHMSM(0,0,0,300);
  78. while (DEF_TRUE){
  79. assignWork(SCIOutPut1,"aaaaaaaaaaaa");
  80. assignWork(SCIOutPut1,"bbbbbbbbbbbb");
  81. assignWork(SCIOutPut2,"cccccccccccc");
  82. assignWork(SCIOutPut2,"dddddddddddd");
  83. assignWork(SCIOutPut1,"eeeeeeeeeeee");
  84. OSTimeDlyHMSM(0,0,0,300);
  85. }
  86. }
  87. static void SCIOutPut1(void *msg){
  88. OS_TCB tcb;
  89. INT8U buf[100];
  90. char *s = msg;
  91. INT16U len;
  92. OSTaskQuery(OS_PRIO_SELF,&tcb);
  93. OSSchedLock();
  94. len = sprintf(buf,"This is work1;worker's priority:%d;process:%s\r",tcb.OSTCBPrio,s);
  95. OSSchedUnlock();
  96. SCI_PutCharsB_Mutex(SCI0,buf,len,0);
  97. OSTimeDlyHMSM(0,0,0,20);
  98. }
  99. static void SCIOutPut2(void *msg){
  100. OS_TCB tcb;
  101. INT8U buf[100];
  102. char *s = msg;
  103. INT16U len;
  104. OSTaskQuery(OS_PRIO_SELF,&tcb);
  105. OSSchedLock();
  106. len = sprintf(buf,"This is work2;worker's priority:%d;process:%s\r",tcb.OSTCBPrio,s);
  107. OSSchedUnlock();
  108. SCI_PutCharsB_Mutex(SCI0,buf,len,0);
  109. OSTimeDlyHMSM(0,0,0,20);
  110. }

70 4

然后把工作布置的密集点就可能发生工作队列满的情况

  1. static void AppTaskStart (void *p_arg){
  2. ……
  3. while (DEF_TRUE){
  4. assignWork(SCIOutPut1,"aaaaaaaaaaaa");
  5. assignWork(SCIOutPut1,"bbbbbbbbbbbb");
  6. assignWork(SCIOutPut2,"cccccccccccc");
  7. assignWork(SCIOutPut2,"dddddddddddd");
  8. assignWork(SCIOutPut1,"eeeeeeeeeeee");
  9. assignWork(SCIOutPut2,"ffffffffffff");
  10. assignWork(SCIOutPut1,"gggggggggggg");
  11. assignWork(SCIOutPut1,"hhhhhhhhhhhh");
  12. OSTimeDlyHMSM(0,0,0,300);
  13. }
  14. }

70 5
可以看到,由于布置工作的任务的优先级最高,而我现在的工作队列实现是手写的队列而且大小为5,所以最开始出现了3个full就是因为连着布置8个任务的后3个放不进去了,然后主任务休息后,工人们开始按照优先级取出工作执行。

更新历史

2018/08/17 发布1.0版本

发表评论

表情:
评论列表 (有 0 条评论,260人围观)

还没有评论,来说两句吧...

相关阅读

    相关 任务/线

    考虑单线程和一个任务一个线程的不足(线程创建销毁开销,对资源的竞争) Executor 异步,任务提交和执行解耦,考虑执行策略, 任务独立(不独立导致线程饥饿死锁,运行中任