BREW平臺上的多任務實現

        BREW的執行環境是基於事件的並且不支持多線程,長時間的任務往往會導致重啓,因此不推薦使用長時間的任務。那麼在實際的開發過程中我們如何解決這一問題呢?幸運的是BREW與生俱來的回調機制爲我們提供瞭解決方案。本文將介紹BREW環境下實現多任務協調運行的方法,實際上也是爲快速簡單的進行多任務應用的開發提供了一個框架。 

BREW的回調機制

        在BREW平臺上同一時刻只能運行一個applet並且只能執行一個線程,運行太長的任務會阻斷系統對消息隊列的訪問,從而導致用戶界面(UI)沒有響應。因此大多數設備上都運行了一個帶有看門狗(watchdog)的RTOS(REX RTOS),定時檢查系統運行的線程,如果一個應用程序佔用了太多的CPU資源導致手機上其它的任務不能運行,手機設備將會重啓。BREW提供了一種回調機制來把長的任務分割成許多短的任務,可以避免手機的重啓,同時我們也能夠在這些短任務和消息處理之間實現有效的控制。

爲了實現這樣的功能需要在shell中註冊回調函數:

Step1. 填充AEECallback結構體:

   AEECallback cb;
   cb.pfnCancel 
= (void *)NULL;   //updated by shell 
   cb.pfnNotify = ResumeNotifyCB; // address of the 
                                  
// callback function 
   cb.pNotifyData = (void *)pMe;  //data to pass

也可以用BREW提供的輔助函數來實現填充:

   void CALLBACK_Init( AEECallback * pcb, 
                       PFNNOTIFY pfn, 
                       
void * pd);

Step2. 調用 ISHELL_Resume.

        在下一次消息處理的時候向回調函數發送一個控制信息,把這個回調函數加入未執行操作的列表中。如果這個回調函數已經註冊了,就取消它並重新註冊。

ISHELL_Resume (pMe->a.m_pIShell, &cb);

如果一個回調函數已經註冊但沒有被執行,那麼可以用下面的方法把它取消:

if(cb.pfnCancel) cb.pfnCancel(&cb);

或者使用輔助函數:

void CALLBACK_Cancel(AEECallback * pcb);

這樣就允許我們進行多任務的協調,如下:

static void Task0 (void *pi)
{
   CIShellApp 
*pMe = (CIShellApp *)pi; 
   pMe
->callBack0_.pfnCancel = (void *)NULL;
   pMe
->callBack0_.pfnNotify = Task0;
   pMe
->callBack0_.pNotifyData = (void *)pMe;
   executeTask0JobAtStep (pMe
->step_); //task related functionality
   pMe->step_++;
   ISHELL_Resume (pMe
->a.m_pIShell, &pMe->callBack_);
}


//other tasks executeTaskNJobAtStep (pMe->step_);

一個簡單的多任務框架

多任務的實現需要處理的東西太多也非常麻煩,比如創建任務、刪除任務和任務跟蹤等等,這些還沒有考慮加入定時器和支持通報的功能。實際上,考慮到線程中上下文切換、同步和數據移動會導致效率低下,即使在一個多線程的平臺上基於回調的解決方案也會更具有吸引力。

該框架的實現主要包含以下類:

1. Dispatcher - 註冊任務並通過回調來執行任務;
2. Callback   - Callback通常和Task配對使用來實現平臺相關的操作,對於用戶來說是透明的;
3. Task       - Task 必須由開發者來實現,它包含應用程序特殊的邏輯。Task類實現了ITask接口,框架中所有任務 

 管理都是通過這個接口來實現的。

各個類之間的交互:

1. Dispatcher註冊所有的Task並且爲每個Task創建一個Callback;
2. Dispatcher用Task來完成所有的操作(簡化版本中實現了start, startTask, stop, stopTask);
3. Callback只要一開始運行就支持Task和系統的交互,他們把所有Task可能關係的時間都通知Dispatcher(例如當Task結束時Task列表中便創建了一個空的位置);
4. Task和框架中的其它部分的聯繫很鬆散。通常一個Task是一個跟蹤自身狀態並且實現需要程序邏輯的狀態機。

使用情形如下:
a. 開發者實現一組Task集合;

每個Task都實現ITask接口:

class ITask 
{
public:
   
virtual EXEC_STATUS execute() = 0;
   
virtual ~ITask(){};
}
;

其中的EXEC_STATUS是一個簡單的枚舉形變量:

const static enum EXEC_STATUS {CONTINUE, STOP}

下面是一個簡單的Task實現:

AsyncTask2::AsyncTask2( IShell* shell, int lineNo) :
                   pos_(
0), lineNo_(lineNo)
{
   writer_ 
= new Writer(shell);
}


EXEC_STATUS AsyncTask2::execute()
{
   pos_ 
+= 10;

   
if (pos_ < 30001)
   
{
      doStepJob();
      writer_
->writeIntAtLine(pos_, lineNo_);
      
// signal intention to continue activity 
      
// requests to be registered for a new task slice
      return CONTINUE;
   }

   
// requests termination of current task
   return STOP;
}


void doStepJob()
{
//do real stuff here.
}


AsyncTask2::
~AsyncTask2() 
{
   delete writer_;
}
;

其中Writer是一個和BREW平臺相關的對象,提供打印服務,pos_是狀態變量。

b. 創建一個Dispatcher;

dispatcher_ = new Dispatcher(m_pIShell);

在全局的InitAppData函數中創建一個Dispatcher並傳入IShell的引用。

c. Dispatcher註冊了所有的Task;

dispatcher_->registerTask(new AsyncTask1(m_pIShell)) ;

//other tasks registered.

d. Dispatcher開始執行活動的Task;

 dispatcher_->start();

e. Task運行;
f. Task可以各自停止或運行,也可以通過Dispathcer來停止或運行

 dispatcher_->stop();

 g. 刪除Dispatcher

delete dispatcher_;

 

框架的內部結構

class Dispatcher 
{
 
public:
  
   Dispatcher(IShell
* shell);
   
int registerTask(ITask* task);
   
void start();
   
void startTask(int i) ;
   
void stop();
   
void stopTask(int caller);
   
~Dispatcher();

 
private:
   
void loop(doCurrentIndexJob job);
   
void initTask(int i); 
   
static boolean isOutOfBoundaries(int pos);
   
int addTask(ITask* task, int position);

 
private:
   Dispatcher();
   Dispatcher(
const Dispatcher&);
   Dispatcher
& operator=(const Dispatcher&);
   
 
private:
   IShell
* shell_;
   CallbackHandle
* cbkHolder_[POS_LIMIT];
   
int nextAvailablePosition_;
}
;

注意:爲了保持例子的簡單性,cbkHolder_的實現只使用了數組而沒有使用更靈活的容器類。前面提到過的start/stop操作都是通用性的操作,通過startTask/stopTask影響到所有的任務,例如:

void Dispatcher::startTask(int i) 
{
   
if (cbkHolder_[i])  cbkHolder_[i]->start();
}


void  Dispatcher::start() 
{
   loop(startTask);
}


這裏loop的參數是一個函數指針

typedef void (Dispatcher::*doCurrentIndexJob)(int pos);

註冊任務:

int Dispatcher::registerTask(ITask* task) 
{
   
int err = addTask(task, nextAvailablePosition_);
   
if (err == SUCCESS)  ++nextAvailablePosition_;
   
return err;
}

nextAvailablePosition是一個內部的計數器,記錄了存放Callback結構的下一個可用單元,如果addTask失敗是不能增加這個計數器的。

int Dispatcher::addTask(ITask* task, int position) 
{
   
if (!task)
      
return EFAILED;
   
if ( 
      isOutOfBoundaries(position)
      
||
      cbkHolder_[position]
      )
   
{
      delete task;
      
return EFAILED;
   }

   cbkHolder_[position] 
= 
          
new CallbackHandle(shell_, task, this, position);
   
if (!cbkHolder_[position] )   return EFAILED;
   
return SUCCESS;
}

下面幾種情況可能會導致addTask失敗:
1.任務沒有初始化;
2.位置越界;
3.當前位置已經有一個活動的回調

如果出現上面幾種情況的任何一種,函數會返回BREW的錯誤代碼。

下面是最有意思的Callback的實現部分:

class CallbackHandle 
{
public:
   CallbackHandle( IShell
* shell, ITask* task, 
                   Dispatcher
* d, int position) : 
         shell_( shell), task_(task), dispatcher_(d), 
                 position_(position)
   
{
      
   }

   
virtual ~CallbackHandle() 
   
{
      release();
      delete task_;
      task_ 
= 0;   
   }


   
void start()
   
{
      CALLBACK_Init(
&cbk_, executeStatic, this);
      executeResume(
this);
   }


    
private
   
static void executeStatic(CallbackHandle* cb)
   
{
      ITask
* t = cb->task_;
      
if (!t) return;
      Dispatcher
* d = cb->dispatcher_;
      
switch (t->execute())
      
{
      
case CONTINUE:
         executeResume(cb);
         
return;
      
case STOP:
         d
->stopTask(cb->position_);
         
return;
      
default:
         d
->stopTask(cb->position_);
         
return;

      }

   }


   
static void   executeResume(CallbackHandle* cb)
   
{
      ISHELL_Resume (cb
->shell_, &cb->cbk_);
   }


   
void release()
   
{
      CALLBACK_Cancel(
&cbk_);
   }

   
private:
   CallbackHandle();
   CallbackHandle(
const CallbackHandle&);
   CallbackHandle
& operator=(const CallbackHandle&);
   
private:
   ITask
* task_;
   IShell
* shell_;
   AEECallback  cbk_;
   Dispatcher
* dispatcher_;
   
int position_;
}
;

        Callback在回調棧中保存它的位置、Task和Dispatcher的引用,它包裝了BREW的回調機制,也就是說往其它平臺上移植的時候只有這個類需要改變。

        executeStatic()是真正工作的函數,這個方法在CALLBACK_Init中被註冊,它根據和它相關的Task的狀態來選擇執行任務或停止任務。

void Dispatcher::stopTask(int caller)
{
   
if (isOutOfBoundaries(caller))
      
return;
   delete cbkHolder_[caller];
   initTask(caller);
   
--nextAvailablePosition_;
}

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