Symbian的异步框架
Active Objects
Active Object是一套事件驱动的多任务模型。在Symbian的标准线程中(除掉一些Java构造线程、原生C构造线程,等),都包含有一个消息循环,在循环中,会不停的查询注册在该线程所属的CActiveScheduler各个ActiveObject的TRequestStatus状态,一旦发现可执行的任务,立马激活并执行。经典的循环伪码如下:
// *** in the loop ***
User::WaitForAnyRequest();
FOREVER
{
if(activeObject->IsActive() && activeObject->iStatus != KRequestPending)
{
activeObject->iActive = EFalse;
TRAPD(r, activeObject->RunL());
if( r != KErrNone )
{
r = activeObject->RunError();
if( r != KErrNone )
Error(r);
}
break;
}
}
一码解千语。在消息循环中,会等待事件激活,接着,会遍历查询注册各个Active Object的状态。当然,简单的遍历是不够体面的,每个Active Object都是带着优先级来的,毫无疑问,优先级高的会被优先考虑执行,低的永远也超越不了。默认,大家都使用EPriorityStandard,一切太平。如果,一个Active Object是需要长时间被执行的,可以考虑使用低优先级,EPriorityIdle、EPriorityLow,这样执行的频率会低一些。而如果任务是具有一些实时要求的,就需要使用高的优先级,亦如,EPriorityUserInput、EPriorityHigh。
但这个模型,是不足够满足实时要求的,因为它是非抢占式的。一个低优先级的任务,被调度了,在那里磨叽磨叽,你优先级再高也奈何不了。在这种模式下,要保持良好的响应能力,需要自律。首先,如果一个任务的实时性高,那么就需要调高它执行线程或进程的优先级,让它优先被CPU调度,不给低优先级任务执行的机会。还有,就是不要在Active Object的RunL中放置执行效率低下的代码(如果是非主线程,那就看菜下饭了...),它会使得该线程失去响应其他请求的能力。
每个异步的任务,都需要派生自CActive类。如果用系统向导,可以发现,Symbian希望每个CActive的子类,做以下几件事情:
实现RunL,放置等待回调一方的执行代码;
实现DoCancel方法,把未有机会到达目的地的任务妥善安置,这是一个模板方法模式,它会被放在Cancel,仅在状态为执行时,会被调用;
实现RunError,在执行出错后,给该CActive子类一次自我救赎的机会;
尝试告诉使用者,这世界没有免费的午餐,需要用SetActive将请求发送给那个执行者,让它帮你搞定问题。
任务驱动的控制核心在于TRequestStatus,执行一方在接到任务后,会将TRequestStatus设置成为KRequestPending,当这个状态被再次改变了,才会触发CActiveScheduler调度执行回调。整个流程如下图所示(依然是盗窃来的...):
/NewsFiles/2009-6/18/811595826.bmp
Client-Server框架
Client-Server框架,是Symbian的重要机制。在Symbian的内核层面,大量使用该模式,将文件管理,界面管理等功能都剥离到了各个服务中,呈微内核态势。所谓Client-Server框架,就是功能调用者Client,和功能执行者Server,各据一方,处于不同的线程或进程。它们搭建在Active Object上,通常通过异步模式进行调用。不过,Client-Server并没有对执行模式进行约束,比如文件服务,就具有同步和异步两种调用模式,当然,本质上换汤不换药,所谓同步只是阻塞异步来实现的。
Client-Server分同进程和跨进程两种,不用说,通信模式完全不一样。在跨进程模式下,需要通信,就需要在Client和Server之间建立一个Session。Session是对Symbian IPC的封装,用协定方式进行通信,只不过,大数据的传输还需要各行其道各显神通。而同进程下,传递信息就简单多了,很多时候,同用一个指针就好。在跨进程模式下,适合集中控制,统一管理资源,特适合想文件服务这样的东东。而同进程,则适合大数据的传输,和逻辑的运算,各开各的线程,各跑各的逻辑。
结语
和同步相比,异步无疑是残酷的,但有了这些框架,比吭哧吭哧动手搞线程,搞通信来的开心多了。