活动对象的一些解释

by mayflowers

 

首先,充分利用 CPU 时间的意思是说,尽可能的少用 CPU 时间,让 CPU
的使用尽可能有意义。这样其它程序或者你的程序的其它部分就能得到执行的
机会。不是说你的程序不管有事没事都要尽可能多的使用 CPU。

第二,使用活动对象的原因是线程的代价太高,线程之间同步的逻辑比较复杂。
其实活动对象本身并不太适合长线任务 —— 需要大量 CPU 时间的任务。更适
用于需要 I/O 资源,但是对 CPU 使用比较少的任务。这个东西要从"中断"
产生的原因说起,你可以去查一下。简单说,比如你要读文件,但是怎么知道
什么时候读完了呢?忙等待。你用一个循环不断的检查文件有没有读完。这非常
充分的使用了CPU,但是绝对不是充分利用了CPU,因为全都是无效操作。(跳
过进线程同步操作问题...)用活动对象不需要这样等待,你调用一个异步操作,
然后 CPU 就是放出来了。等到操作完成的时候,操作系统会通知你,你就可以
执行后续的处理。这是活动对象的优点。但是象刚才所说,如果你的任务是纯粹
的计算任务,用这种方法没有任何好处。走走停停反而降低效率。这时候多线程
是比较好的选择。使用长线任务只是不方便使用多线程时候的一个折中方案。

第三,什么是长线任务?为什么长线任务不能直接计算完成?长线任务指的是
需要长期、连续使用 CPU 才能完成的任务。长线任务长期占用CPU,对于单
线程应用来说,其它活动对象就得不到CPU。所有的用户操作、文件读写等活
动对象都得不到处理机会。对用户而言就是应用停止响应,底层的一些会话还
有可能超时被服务器关闭。这都是不应该的。将这种大量消耗CPU资源的任务
切分成片段逐次运行,目的就是为了出让CPU,让其它活动对象得到执行机会。
活动对象调度器的调度方式决定了,你的长线任务必须是最低优先级,而且不
能同时有两个长线任务。

第四,似乎你对打印操作有一点儿误解 打印是一个被多次执行的操作,但无
论如何不是长线任务。即使动画每几十毫秒画一次屏幕,也不是长线任务。它只
是一个频繁被执行的任务,它对 CPU 的要求很低。



异步函数的同步调用

一、使用CActiveSchedulerWait类

        在以前的文章"Symbian编程总结-界面篇-打开jpeg/gif/png图像"里我们已经看到了CActiveSchedulerWait类的使用方法,在此我再详细介绍一下。

        很多初学者在开始时会将CActiveScheduler和CActiveSchedulerWait类弄混,CActiveScheduler是活动对象的调度器,而CActiveSchedulerWait可以简单的理解为一个当前线程的阻塞器

  • 调用CActiveSchedulerWait::Start()方法时,线程阻塞;
  • 调用CActiveSchedulerWait::AsyncStop()方法时,请求停止对线程的阻塞

        因此,我们在不修改原来活动对象代码的情况下,只要简单的在异步函数调用方法后加上"CActiveSchedulerWait::Start()",在活动对象的RunL方法的开头加入"CActiveSchedulerWait::AnsycStop()"就可以了。

        针对上一节教程介绍的控制台应用程序,我们对以下几个方法(下划线为修改部分)进行修改:

 

点击此处下载源代码

 

CActiveSchedulerWait* iWait;

 

void CMyActiveObject::ConstructL()

    {

    User::LeaveIfError(iTimer.CreateLocal() ); // Initialize timer

    CActiveScheduler::Add( this); // Add to scheduler

 

    iWait = new (ELeave)CActiveSchedulerWait;

    }

 

CMyActiveObject::~CMyActiveObject()

    {

    Cancel(); // Cancel any request, if outstanding

    iTimer.Close(); // Destroy the RTimer object

    // Delete instance variables if any

 

    if (iWait->IsStarted())

        {

        iWait->AsyncStop();

        }

 

    delete iWait;

    iWait = NULL;

    }

 

void CMyActiveObject::StartL(TTimeIntervalMicroSeconds32 aDelay)

    {

    Cancel(); // Cancel any request, just to be sure

 

    iTimer.After(iStatus, aDelay); // Set for later

    SetActive(); // Tell scheduler a request is active

 

    iWait->Start(); // 1

    }

 

void CMyActiveObject::RunL()

    {

    iWait->AsyncStop(); // 2

 

    TBuf<50> outputStr;

    outputStr.AppendNum(iCount);

    iConsole.Write(outputStr);

    iConsole.Write(_L("/n"));

    iCount++;

    }

 

    使用CActiveSchedulerWait的几点注意事项:

  1. CActiveSchedulerWait必须结合活动对象使用,而且使用方法只有以上代码介绍的那一种;
  2. Start方法和AsyncStop方法必须成对出现;
  3. 程序退出时要检查CActiveSchedulerWait是否在IsStarted()状态,如果是则调用AsyncStop方法。否则程序不能正常退出;
  4. CActiveScheduler类内部有自己的静态指针,提供的静态方法都调用了内部的静态指针。而CActiveSchedulerWait类没有内部静态指针,方法也不是静态的,我们必须自己管理CActiveSchedulerWait类的全局指针,在这点上程序要经过良好的设计。

 

二、使用User::WaitForRequest方法

    如果不想使用活动对象,也不想使用难于管理的CactiveSchedulerWait,你可以使用User::WaitForRequest方法。以下是User::WaitForRequest方法的原型:

IMPORT_C static void WaitForRequest(TRequestStatus& aStatus);

        此方法将等待异步函数服务器返回的信号量,然后匹配aStatus参数。如果接收到的信号与参数aStatus一一匹配,则跳过阻塞进入下一行代码,否则继续阻塞线程直到aStatus对应的信号通知返回。

User::WaitForRequest还有一个重载的方法,它可以监视两个信号的通知:

IMPORT_C static void WaitForRequest(TRequestStatus& aStatus1,TRequestStatus& aStatus2);

         有了User::WaitForRequest,异步函数使用起来就变得非常方便,我们不需要创建活动对象,也不需要创建成员变量TRequestStatus,只需要声明局部的TRequestStatus、局部的异步函数类,在异步函数调用之后,加入User::WaitForRequest(status),就能够使线程阻塞在User::WaitForRequest处直到status对应的异步函数处理完成。

LOCAL_C void DoTestL()

    {

    RTimer timer;

    CleanupClosePushL(timer);

    User::LeaveIfError(timer.CreateLocal());

   

    TRequestStatus status(KRequestPending);

   

    // 调用异步方法并将status传入

    timer.After(status, 1000000);

   

    // 等待status对应的信号量,此处用了User::WaitForRequest方法将异步方法的调用模拟成同步

    User::WaitForRequest(status);

    CleanupStack::Pop(&timer);

    }

 

LOCAL_C void MainL()

    {

    TInt n = 0;

    TBuf<10> str;

    _LIT(KNewLine, "/n");

   

    FOREVER

        {

        DoTestL();

        str.Num(n);

        n++;

       

        console->Write(str);

        console->Write(KNewLine);

        }

    }

 

三、使用User::WaitForRequest方法的问题

        User::WaitForRequest有时候不会正常运行,如:CImageDecoder::Convert方法:

IMPORT_C virtual void Convert(TRequestStatus* aRequestStatus, CFbsBitmap& aDestination, CFbsBitmap& aDestinationMask, TInt aFrameNumber = 0);

        最后一个参数必须为EOptionAlwaysThread,User::WaitForRequest才能正常执行,个人认为CImageDecoder::Convert如果没有加EOptionAlwaysThread参数的时候是使用"长线任务"(将在下一节介绍)实现的。所以,CImageDecoder::Convert方法应该按照如下方法调用:

    TRequestStatus status(KRequestPending);

    CImageDecoder* decoder = CImageDecoder::FileNewL(iFs, aFileName, KMIMEType);

   

    decoder->Convert(&status, aBitmap, CImageDecoder::EOptionAlwaysThread);

    User::WaitForRequest(status);

   

    delete decoder;

    decoder = NULL;


主动结束活动对象
by qxiaoyuan
这个事件是否你自己产生的, Cancel函数的实现除了调用DoCancel外, 还有一个操作一般人不知道, 就是它会调用User::WaitRequest, 对于client/server模式, 收到cancel请求后, 一样会发送event, 只不过状态改为KErrCancel, 而cancel函数就是来截获这个事件, 一般不用关心, 但是当自己实现event时, 尤其是模拟事件机制时, 经常不会发送这个事件, 导致Cancel等待不会被发送的事件, 表现就是系统挂起
正宗做法是给server发送cancel消息, 一个是减少资源浪费, 一个是避免server到时发送事件没有AO接受导致E32User-CBase 46.
那么 server 收到 Cancel命令后取消当前请求, 并且给client事件, 只是把状态设置为KErrCancel. 就ok了.
比如对RSocket, 你前面发出请求 RecvOneOrMore, 那么在定时器超时了, 你要取消操作, 就是调用 Cancel,
在DoCancel 里调用 CancelRecv, 该函数的实现通知服务器取消接收操作, 并且掉 RMessage2::Complete(KErrCancel) 通知客户端, 然后 Cancel在 DoCancel结束后调User::WaitRequest等待这个事件.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值