Active Object (AO) 框架,是Symbian的基本工作部分。它是为了满足多个任务同时执行的要求。在 Windows/Unix 平台上,我们可以不加思索的使用多线程来完成多任务。可是在嵌入式平台上,系统的资源是有限的。比如CPU、内存都比我们平时用的个人计算机要低。这就要求嵌入式系统能够合理的使用系统资源。不能频繁的切换线程或者进程。
Symbian为这种特别需求设计了Active Object (AO)框架。AO框架是运行于一个线程内部的调度框架。其基本思想就是把一个单线程分为多个时间片,来运行不同的任务。
这和多线程有很大区别。多线程之间是可以被抢占的(由操作系统调度),但是AO框架中的各个任务是不可被抢占的,一个任务必须完成,才能开始下一个任务。
下面是多线程和AO框架的简单比较:
多线程 AO框架
可以被抢占 不可被抢占
上下文切换耗费CPU时间 没有上下文切换
由操作系统调度 由线程自己的AO框架调度
每个线程都有至少4K Stack. AO没有单独的Stack.
操作系统还要分配额外的资源记录线程 只是一个Active Object.
Symbian系统本身使用了大量的AO框架来实现一些系统服务。这使得Symbian和其他嵌入式系统相比较,对系统资源的使用更加合理。
AO框架包括CActiveScheduler 和CActive (Active Object)。一个线程的所有的Active Object对象都被安装在该线程的CActiveScheduler对象内.由CActiveScheduler对象监控每个Active Object是否完成了当前任务,如果完成了,就调度下一个Active Object来执行。CActiveScheduler根据优先级来调度各个Active Object.
关于CActiveScheduler的创建和安装,CActive的创建和安装,和CActive的任务处理,可以参看 SDK 文档。理解起来不难。下面要说一个比较容易忽略的地方。这对理解AO框架非常重要。
创建调度器:
CActiveScheduler * scheduler = new (ELeave) CActiveScheduler;
CleanupStack::PushL(scheduler);
CActiveScheduler::Install(scheduler);
运行调度器:
CActiveScheduler::Start();
停止调度器:
CActiveScheduler::Stop();
以上代码都是运行在一个线程中的,一般来讲,一个EXE只有一个主线程.
可是如果真的有2个线程呢?
为什么在当前线程下调用CActiveScheduler::Start(),CActiveScheduler::Stop()运行/停止的就是当前线程的调度器而不是另一个线程的调度器?
每个线程都有自己的CActiveScheduler,那么这个CActiveScheduler类是怎么调用CActiveScheduler::Start(),CActiveScheduler::Stop()来运行/停止当前的调度器的呢?
我们看到Start/Stop并没有参数.
打开CActiveScheduler的类定义:
class CActiveScheduler : public CBase
{
public:
IMPORT_C CActiveScheduler();
IMPORT_C ~CActiveScheduler();
IMPORT_C static void Install(CActiveScheduler* aScheduler);
IMPORT_C static CActiveScheduler* Current();
IMPORT_C static void Add(CActive* anActive);
IMPORT_C static void Start();
IMPORT_C static void Stop();
IMPORT_C static TBool RunIfReady(TInt& aError, TInt aMinimumPriority);
IMPORT_C static CActiveScheduler* Replace(CActiveScheduler* aNewActiveScheduler);
IMPORT_C virtual void WaitForAnyRequest();
IMPORT_C virtual void Error(TInt anError) const;
private:
void DoStart();
void OwnedStartLoop(TInt& aRunning);
IMPORT_C virtual void OnStarting();
IMPORT_C virtual void OnStopping();
IMPORT_C virtual void Reserved_1();
IMPORT_C virtual void Reserved_2();
private:
// private interface used through by CActiveSchedulerWait objects
friend class CActiveSchedulerWait;
static void OwnedStart(CActiveSchedulerWait& aOwner);
protected:
inline TInt Level() const;
private:
TInt iLevel;
TPriQue<CActive> iActiveQ;
};
我们并没有看到静态的成员来表示线程,但是却有一个static函数CActiveScheduler* Current();返回当前线程的调度器.
现在猜想奥秘就在这个函数是怎么实现的。这个静态的函数怎么就能得到当前这个运行线程的调度器,而不是别的线程的调度器。我们可以猜想,肯定是Current()内部实现能取到当前线程的标识信息.用这个标识,静态函数能取到这个线程的CActiveScheduler.这个具体如何实现呢?
答案就是:当前线程的线程ID可以这样得到:
创建一个缺省的线程对象:
RThread thread;
取得当前线程的ID:
TThreadId threadId = thread.Id();
能得到当前线程的threadId,当然可以得到和当前线程关联的CActiveScheduler。因此以上两个问题也就迎刃而解了,在一个线程内调用CActiveScheduler::Start(),CActiveScheduler::Stop()开启的就是当前线程。
既然回复了上面的问题,那么我们自然就能明确,在一个线程内是不能通过Start和Stop函数来开启和停止另一个线程内的活动对象规划器CActiveScheduler。
补充点其他东西:
在Symbian操作系统中,每个进程都有一个或多个线程。线程是执行的基本单位。一个进程的主线程是在进程启动时生成的。Symbian属于抢占式多任务操作系统,这意味着每个线程都有自己的执行时间,直到系统将CPU使用权给予其他线程。当系统调度时,具有最高优先权的线程将首先获得执行。
进程边界是受内存保护的。所有的用户进程都有自己的内存地址空间,同一进程中的所有线程共享这一空间,用户进程不能直接访问其他进程的地址空间。
每个线程都有它自己的stack和heap,这里heap可以是私有的,也可以被其他线程共享。应用程序框架生成并安装了一个active scheduler,并且为主线程准备了清除栈。如果没有使用框架(如编写exe程序)那就要手动生成这些了。
Symbian操作系统专为单线程应用优化,因此强烈推荐使用“活动对象”代替多线程。
posted on 2008-10-11 20:34
frank.sunny 阅读(2665)
评论(2) 编辑 收藏 引用 所属分类:
symbian 开发