前言

线程池在软件开发中应用的很广泛,其很适合处理需要并行(并发)处理大量类似任务的情况。比如在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){}来阻塞等待结果。

下面几张图表示出了任务池的整个工作流程(好萌的小人):
基于uCOS-II的任务池/线程池 模块
基于uCOS-II的任务池/线程池 模块
基于uCOS-II的任务池/线程池 模块
基于uCOS-II的任务池/线程池 模块

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

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 
                                         // 任务池中每个任务的栈大小

#define TP_WORKQ_SIZE           5        // the size of the work-queue (if there is no task available,the 
                                         // new work will be put in the work-queue wating to be process).
                                         // 工作队列的大小(如果没有空闲任务,新的工作将放入工作队列中
                                         // 等待被提走处理)

#define TP_ARGUMENT_CHECK_EN    TRUE     // TRUE: arguments will be checked, however,this will 
                                         //       cost a little code volume.

// choose the implementation of internal workqueue(SEE TP_WORKQ_IMP_XXXXX)
// 选择工作队列的内部实现方式
#define TP_WORKQ_IMP       TP_WORKQ_IMP_QUEUE_OS_SEM

// for OS_MEM implementation of memory control(TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_PLUS_MEM)
// reserve how many space for work-items for dynamic allocation.
// 对于使用OS_MEM来实现内存控制这种情况时,要预留多少个工作项的空间来动态分配
#define TP_WORKM_SIZE      (TP_WORKQ_SIZE + TP_TASK_COUNT)

/*
*********************************************************************************************************
*                                  FUNCTION PROTOTYPES 函数原型
*********************************************************************************************************
*/

INT8U TP_Init(void);
INT8U TP_AssignWork(void (*work)(void *att),void *att);

/*
*********************************************************************************************************
*                                    ERROR CHECK
*********************************************************************************************************
*/

#if(TP_WORKQ_IMP != TP_WORKQ_IMP_OS_Q_PLUS_MEM && \
    TP_WORKQ_IMP != TP_WORKQ_IMP_OS_Q_STD_MEM && \
    TP_WORKQ_IMP != TP_WORKQ_IMP_QUEUE_OS_SEM)
    #error choose one implementation of internal workqueue(TP_WORKQ_IMP)
#endif


#endif
/*
*********************************************************************************************************
*
*
*                                        Pool of Tasks for uCOS-II
*                                             uCOS-II 任务池
*
* File : TaskPool.c
* 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.
* History : 2018/08/08   the original version of TaskPool.
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                     INCLUDES
*********************************************************************************************************
*/

#include "TaskPool.h"
#include <stddef.h>
#if(TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_STD_MEM)
#include <stdlib.h>
#endif

/*
*********************************************************************************************************
*                                    DATA TYPE    数据类型
*********************************************************************************************************
*/

typedef struct work_item{
  void (*work)(void *att);           // the work to do
  void *att;                         // attachment of the work
} WORK_ITEM,* pWORK_ITEM;


/*
*********************************************************************************************************
*                                LOCAL FUNCTION DECLARATION
*********************************************************************************************************
*/

#if(TP_ARGUMENT_CHECK_EN == TRUE)
  #define argCheck(cond,rVal)  if(cond) {return (rVal); }
#else
  #define argCheck(cond,rVal)
#endif // of (TP_ARGUMENT_CHECK_EN == TRUE)

static void _WorkerTask(void *p_arg);

// work-queue
// return: TRUE   success
//         FALSE  any error
static INT8U _WorkQueue_Init(void);
// return: TRUE   success
//         FALSE  any error
static INT8U _WorkQueue_Out(pWORK_ITEM rst);
static INT8U _WorkQueue_In(void (*work)(void *att),void *att);


/*
*********************************************************************************************************
*                                    LOCAL VARIABLE 
*********************************************************************************************************
*/
// workers's task
static INT8U WorkersStk[TP_TASK_STACK_SIZE * TP_TASK_COUNT];


/*
*********************************************************************************************************
*                                        Task Pool initialization
*
* Description : To initialize the task pool.    初始化任务池
*
* Arguments   : 
*
* Return      : TP_ERR_NONE        if success                           成功
*               TP_ERR_TASK_INIT   if err when initialize tasks         初始化任务时发生问题
*               TP_ERR_WORKQ_INIT  if err when initialize work queue    初始化工作队列时出现问题
*Note(s):
*********************************************************************************************************
*/

INT8U TP_Init(void){
  INT8U i,err;
  // create tasks for all workers
  for(i = 0;i < TP_TASK_COUNT; i++){
#if (OS_TASK_CREATE_EXT_EN > 0)
    err = OSTaskCreateExt(_WorkerTask,
                    NULL,
                    (OS_STK *)&WorkersStk[TP_TASK_STACK_SIZE * (i + 1) - 1],
                    TP_TASK_PRIO_START + i,
                    TP_TASK_PRIO_START + i,
                    (OS_STK *)&WorkersStk[TP_TASK_STACK_SIZE * i],
                    TP_TASK_STACK_SIZE,
                    NULL,
                    OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);    
#else
    err = OSTaskCreate(_WorkerTask,
                    NULL,
                    (OS_STK *)&WorkersStk[TP_TASK_STACK_SIZE * (i + 1) - 1],
                    TP_TASK_PRIO_START + i);
#endif
    if(err != OS_ERR_NONE)
      return TP_ERR_TASK_INIT;
  }
  if(!_WorkQueue_Init())
    return TP_ERR_WORKQ_INIT;
  return TP_ERR_NONE;
}
/*
*********************************************************************************************************
*                                      Assign a work to Task Pool
*
* Description : To assign a work to the task pool.    指派工作给任务池
*
* Arguments   : work    the work to do      要做的工作
*               att     attachment of the work(pass some argument to the work)  工作的附件
*
* Return      : TP_ERR_NONE         if success             成功
*               TP_ERR_POINTER_NULL if work == NULL        空指针
*               TP_ERR_MEMORY       if out of memory       内存不够用
*               TP_ERR_WORKQ_FULL   if work queue is full  工作队列满
*               TP_ERR_UNKNOWN      if fatal error         未知错误
* Note(s): 
*********************************************************************************************************
*/
INT8U TP_AssignWork(void (*work)(void *att),void *att){
  argCheck(work == NULL, TP_ERR_POINTER_NULL);
  return _WorkQueue_In(work,att);
}

/*
*********************************************************************************************************
*                                      Worker's Task
*
* Description : Task for workers.    工人们的Task
*
* Arguments   : 
*
* Return      : 
*
* Note(s)     : 
*********************************************************************************************************
*/

// 当前工人的任务就是无限取出工作并执行
static void _WorkerTask(void *p_arg){
  WORK_ITEM item;
  while(TRUE){
    if(_WorkQueue_Out(&item)){   // get next work and
      item.work(item.att);      // do the work
    }
  }
}
/*
*********************************************************************************************************
*                                         WORKQUEUE IMPLEMENTATION
*********************************************************************************************************
*/
#if(TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_PLUS_MEM || TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_STD_MEM)
// memory control
// return: TRUE   success
//         FALSE  any error
static INT8U _MemctrlInit(void);
// return NULL if fail
static pWORK_ITEM _WorkItem_Create(void);
static void _WorkItem_Dispose(pWORK_ITEM p);


static OS_EVENT *WorkQueue;
static pWORK_ITEM WorkPtrs[TP_WORKQ_SIZE];
//static volatile OS_Q *p;    调试时看队列用
static INT8U _WorkQueue_Init(void){
  WorkQueue = OSQCreate(WorkPtrs,TP_WORKQ_SIZE);
  //p = WorkQueue->OSEventPtr;
  return (WorkQueue != NULL) && _MemctrlInit();
}

static INT8U _WorkQueue_Out(pWORK_ITEM rst){
  INT8U err;
  pWORK_ITEM pItem;
  // get next work
  pItem = (pWORK_ITEM)OSQPend(WorkQueue,0,&err);
  if(err != OS_ERR_NONE)
    return FALSE;
  // store the message and return the memory
  *rst = *pItem;
  _WorkItem_Dispose(pItem);
  return TRUE;
}

static INT8U _WorkQueue_In(void (*work)(void *att),void *att){
  INT8U err;
  pWORK_ITEM pItem;
  // get a workitem and store the message
  pItem = _WorkItem_Create();
  if(pItem == NULL)
    return TP_ERR_MEMORY;
  pItem->work = work;
  pItem->att = att;
  err = OSQPost(WorkQueue,pItem);
  if(err == OS_ERR_NONE)
    return TP_ERR_NONE;
  _WorkItem_Dispose(pItem);
  if(err == OS_ERR_Q_FULL)
    return TP_ERR_WORKQ_FULL;
  return TP_ERR_UNKNOWN;
}
#endif

#if(TP_WORKQ_IMP == TP_WORKQ_IMP_QUEUE_OS_SEM)
typedef struct {
  WORK_ITEM Items[TP_WORKQ_SIZE];
#if(TP_WORKQ_SIZE > 255)
  INT16U pIn;          // index of the next item to be put.
  INT16U pOut;         // index of the next item to be picked out.
  INT16U cnt;
#else
  INT8U pIn;
  INT8U pOut;
  INT8U cnt;
#endif
}WORK_QUEUE;
static WORK_QUEUE WorkQueue;
static OS_EVENT *WorkQueueSem;

static INT8U _WorkQueue_Init(void){
  WorkQueueSem = OSSemCreate(0);
  return WorkQueueSem != NULL;
}

static INT8U _WorkQueue_Out(pWORK_ITEM rst){
  INT8U err;
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
  OS_CPU_SR  cpu_sr = 0;
#endif
  // get next work
  OSSemPend(WorkQueueSem,0,&err);
  if(err != OS_ERR_NONE)
    return FALSE;
  OS_ENTER_CRITICAL();
  if(WorkQueue.cnt == 0) {  // conflict with another task
    OS_EXIT_CRITICAL();
    return FALSE;
  }
  WorkQueue.cnt--;
  *rst = WorkQueue.Items[WorkQueue.pOut];
  if(++WorkQueue.pOut >= TP_WORKQ_SIZE)
    WorkQueue.pOut = 0;
  OS_EXIT_CRITICAL();
  return TRUE;
}

static INT8U _WorkQueue_In(void (*work)(void *att),void *att){
  INT8U err;
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
  OS_CPU_SR  cpu_sr = 0;
#endif
  OS_ENTER_CRITICAL();
  if(WorkQueue.cnt >= TP_WORKQ_SIZE) {  // queue full
    OS_EXIT_CRITICAL();
    return TP_ERR_WORKQ_FULL;
  }
  WorkQueue.cnt++;
  WorkQueue.Items[WorkQueue.pIn].work = work;
  WorkQueue.Items[WorkQueue.pIn].att = att;
  if(++WorkQueue.pIn >= TP_WORKQ_SIZE)
    WorkQueue.pIn = 0;
  OS_EXIT_CRITICAL();
  // post new work
  err = OSSemPost(WorkQueueSem);
  if(err == OS_ERR_NONE)
    return TP_ERR_NONE;
  else
    return TP_ERR_UNKNOWN;
}
#endif
/*
*********************************************************************************************************
*                                    MEMORY CONTROL IMPLEMENTATION
*********************************************************************************************************
*/
// memory control by OS_MEM
#if(TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_PLUS_MEM)
static WORK_ITEM WorkEntities[TP_WORKM_SIZE];
static OS_MEM *WorkPool;

static INT8U _MemctrlInit(void){
  INT8U err;
  WorkPool = OSMemCreate(&WorkEntities[0],TP_WORKM_SIZE,sizeof(WORK_ITEM),&err);
  return WorkPool != NULL;
}
static pWORK_ITEM _WorkItem_Create(void){
  INT8U err;
  pWORK_ITEM rst;
  rst = (pWORK_ITEM)OSMemGet(WorkPool,&err);
  if(err == OS_ERR_NONE)
    return rst;
  else
    return NULL;
}
static void _WorkItem_Dispose(pWORK_ITEM p){
  OSMemPut(WorkPool,(void *)p);
}
#endif
// memory control by C standard library
#if(TP_WORKQ_IMP == TP_WORKQ_IMP_OS_Q_STD_MEM)
static INT8U _MemctrlInit(void){
  return TRUE;
}

static pWORK_ITEM _WorkItem_Create(void){
  return (pWORK_ITEM)malloc(sizeof(WORK_ITEM));
}

static void _WorkItem_Dispose(pWORK_ITEM p){
  free((void *)p);
}
#endif

示例程序

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

/*
*********************************************************************************************************
*                                      Example Code for TaskPool
*
* By  : Lin Shijun
*********************************************************************************************************
*/

#include "includes.h"
#include "SCI_def.h"
#include <string.h>
#include "TaskPool.h"
#include <stdio.h>
/*
*********************************************************************************************************
*                                      STACK SPACE DECLARATION
*********************************************************************************************************
*/
static  OS_STK  AppTaskStartStk[APP_TASK_START_STK_SIZE];
/*
*********************************************************************************************************
*                                      TASK FUNCTION DECLARATION
*********************************************************************************************************
*/

static void AppTaskStart(void *p_arg);

/*
*********************************************************************************************************
*                                      LOCAL  FUNCTION  DECLARE
*********************************************************************************************************
*/
// SCI工作
static void SCIOutPut1(void *msg);
static void SCIOutPut2(void *msg);
/*
*********************************************************************************************************
*                                           MAIN FUNCTION
*********************************************************************************************************
*/
void main(void) {
    INT8U  err;    
    BSP_IntDisAll();                                                    /* Disable ALL interrupts to the interrupt controller       */
    OSInit();                                                           /* Initialize uC/OS-II                                      */

    err = OSTaskCreate(AppTaskStart,
                          NULL,
                          (OS_STK *)&AppTaskStartStk[APP_TASK_START_STK_SIZE - 1],
                          APP_TASK_START_PRIO);                     
    OSStart();
}

static void assignWork(void (*work)(void *msg),const char *str){
  switch(TP_AssignWork(work,str)){
    case TP_ERR_NONE:
      break;
    case TP_ERR_POINTER_NULL:
      SCI_PutCharsB_Mutex(SCI0,"pointer null\r",13,0);
      break;
    case TP_ERR_MEMORY:
      SCI_PutCharsB_Mutex(SCI0,"out of memory\r",14,0);
      break;
    case TP_ERR_WORKQ_FULL:
      SCI_PutCharsB_Mutex(SCI0,"wq full\r",8,0);
      break;
    case TP_ERR_UNKNOWN:
    default:
      SCI_PutCharsB_Mutex(SCI0,"fatal err\r",10,0);
      break;
  }//*/
}

static void AppTaskStart (void *p_arg){
  INT8U err;
  (void)p_arg;                                            /* Prevent compiler warning    */
  BSP_Init(); 
  SCI_Init(SCI0);
  SCI_EnableTrans(SCI0);
  SCI_EnableRxInt(SCI0);
  SCI_EnableRecv(SCI0);
  SCI_BufferInit();
  TP_Init();
  OSTimeDlyHMSM(0,0,0,300);
  while (DEF_TRUE){
    assignWork(SCIOutPut1,"aaaaaaaaaaaa");
    assignWork(SCIOutPut1,"bbbbbbbbbbbb");
    assignWork(SCIOutPut2,"cccccccccccc");
    assignWork(SCIOutPut2,"dddddddddddd");
    assignWork(SCIOutPut1,"eeeeeeeeeeee");
    OSTimeDlyHMSM(0,0,0,300);
  }
}

static void SCIOutPut1(void *msg){
  OS_TCB tcb;
  INT8U buf[100];
  char *s = msg;
  INT16U len;
  OSTaskQuery(OS_PRIO_SELF,&tcb);
  OSSchedLock();
  len = sprintf(buf,"This is work1;worker's priority:%d;process:%s\r",tcb.OSTCBPrio,s);
  OSSchedUnlock();
  SCI_PutCharsB_Mutex(SCI0,buf,len,0);
  OSTimeDlyHMSM(0,0,0,20);
}

static void SCIOutPut2(void *msg){
  OS_TCB tcb;
  INT8U buf[100];
  char *s = msg;
  INT16U len;
  OSTaskQuery(OS_PRIO_SELF,&tcb);
  OSSchedLock();
  len = sprintf(buf,"This is work2;worker's priority:%d;process:%s\r",tcb.OSTCBPrio,s);
  OSSchedUnlock();
  SCI_PutCharsB_Mutex(SCI0,buf,len,0);
  OSTimeDlyHMSM(0,0,0,20);
}

基于uCOS-II的任务池/线程池 模块

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

static void AppTaskStart (void *p_arg){
……
  while (DEF_TRUE){
    assignWork(SCIOutPut1,"aaaaaaaaaaaa");
    assignWork(SCIOutPut1,"bbbbbbbbbbbb");
    assignWork(SCIOutPut2,"cccccccccccc");
    assignWork(SCIOutPut2,"dddddddddddd");
    assignWork(SCIOutPut1,"eeeeeeeeeeee");
    assignWork(SCIOutPut2,"ffffffffffff");
    assignWork(SCIOutPut1,"gggggggggggg");
    assignWork(SCIOutPut1,"hhhhhhhhhhhh");
    OSTimeDlyHMSM(0,0,0,300);
  }
}

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

更新历史

2018/08/17 发布1.0版本

--------------------- 本文来自 夏日白云 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/lin_strong/article/details/81777653?utm_source=copy

分类:

技术点:

相关文章: