基於uCOS-II的任務池/線程池 模塊

前言

線程池在軟件開發中應用的很廣泛,其很適合處理需要並行(併發)處理大量類似任務的情況。比如在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){}來阻塞等待結果。

下面幾張圖表示出了任務池的整個工作流程(好萌的小人):



很形象吧,感覺不用多解釋了。
而且當前實現提供的接口也很簡單,就一個初始化以及一個佈置任務。

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);
}

然後把工作佈置的密集點就可能發生工作隊列滿的情況

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);
  }
}


可以看到,由於佈置工作的任務的優先級最高,而我現在的工作隊列實現是手寫的隊列而且大小爲5,所以最開始出現了3個full就是因爲連着佈置8個任務的後3個放不進去了,然後主任務休息後,工人們開始按照優先級取出工作執行。

更新歷史

2018/08/17 發佈1.0版本

--------------------- 本文來自 夏日白雲 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/lin_strong/article/details/81777653?utm_source=copy

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章