franksunny的个人技术空间
获得人生中的成功需要的专注与坚持不懈多过天才与机会。 ——C.W. Wendte

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()开启的就是当前线程。

既然回复了上面的问题,那么我们自然就能明确,在一个线程内是不能通过StartStop函数来开启和停止另一个线程内的活动对象规划器CActiveScheduler

 

补充点其他东西:

Symbian操作系统中,每个进程都有一个或多个线程。线程是执行的基本单位。一个进程的主线程是在进程启动时生成的。Symbian属于抢占式多任务操作系统,这意味着每个线程都有自己的执行时间,直到系统将CPU使用权给予其他线程。当系统调度时,具有最高优先权的线程将首先获得执行。

进程边界是受内存保护的。所有的用户进程都有自己的内存地址空间,同一进程中的所有线程共享这一空间,用户进程不能直接访问其他进程的地址空间。

每个线程都有它自己的stackheap,这里heap可以是私有的,也可以被其他线程共享。应用程序框架生成并安装了一个active scheduler,并且为主线程准备了清除栈。如果没有使用框架(如编写exe程序)那就要手动生成这些了。

Symbian操作系统专为单线程应用优化,因此强烈推荐使用“活动对象”代替多线程。

posted on 2008-10-11 20:34 frank.sunny 阅读(2666) 评论(2)  编辑 收藏 引用 所属分类: symbian 开发

FeedBack:
# re: 从线程角度看AO框架
2008-10-12 07:36 | luke
这个感觉就是一个在用户层调度的线程,类似所谓的“纤程”概念,不知道是否这样?  回复  更多评论
  
# re: 从线程角度看AO框架
2008-10-12 08:19 | frank.sunny
@luke
谢谢luke,你的提点很有用,我对Windows核心编程之前了解的并不多,在你提点下,又去查了下

进程通常被定义为一个正在运行的程序的实例
线程是cpu分配的基本单位
进程是不活泼的。进程从来不执行任何东西,它只是线程的容器。线程总是在某个进程环境中创建的,而且它的整个寿命期都在该进程中。单个进程可能包含若干个线程,所有这些线程都“同时”执行进程地址空间中的代码。
这个可能大家都知道的

纤程是以用户方式代码来实现的,内核并不知道纤程,并且它们是根据用户定义的算法来调度的。由于你定义了纤程的调度算法,因此,就内核而言,纤程采用非抢占式调度方式。
需要了解的下一个问题是,单线程可以包含一个或多个纤程。就内核而言,线程是抢占调度的,是正在执行的代码。然而,线程每次执行一个纤程的代码—你决定究竟执行哪个纤程。
虽然我不知道symbianOS内核开发的时候是怎么样的,但是我根据symbian活动对象的原理来分析,的确应该就是“纤程”的影子,再次感谢luke的提点  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理



常用链接

留言簿(13)

随笔分类

个人其它博客

基础知识链接

最新评论

阅读排行榜

评论排行榜