BREW平台上的多任务实现

http://blog.csdn.net/lonelywinter340/archive/2007/10/29/1854429.aspx

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值