Idle Processing

原创 2001年05月30日 02:19:00

Idle Processing

Because MFC's application class, CWinApp, provides the message loop that retrieves and dispatches messages, it's a simple matter for CWinApp to call a function in your application when no messages are waiting to be processed. If you look at the source code for the CWinThread::Run function that gets called by WinMain to start the message loop, you'll see something that looks like this:

BOOL bIdle = TRUE;
LONG lIdleCount = 0;

for (;;)
{
     while (bIdle &&
         !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
    {
         if (!OnIdle(lIdleCount++))
            bIdle = FALSE;
    }

    do
    {
        if (!PumpMessage())
            return ExitInstance();

        if (IsIdleMessage(&m_msgCur))
        {
            bIdle = TRUE;
            lIdleCount = 0;
        }

    } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}

Before it calls PumpMessage to retrieve and dispatch a message, Run calls ::PeekMessage with a PM_NOREMOVE flag to check the message queue. If a message is waiting, ::PeekMessage copies it to an MSG structure and returns a nonzero value but doesn't remove the message from the queue. If no messages are waiting, ::PeekMessage returns 0. Unlike ::GetMessage, ::PeekMessage doesn't wait for a message to appear in the message queue before returning; it returns immediately. If ::PeekMessage returns nonzero, indicating that messages are waiting to be processed, CWinThread::Run enters a do-while loop that calls CWinThread::PumpMessage repeatedly to retrieve and dispatch the messages. But if ::PeekMessage returns 0 and the bIdle flag is set, CWinThread::Run calls a member function named OnIdle to give the application an opportunity to perform idle processing. Because OnIdle is a virtual function and because CWinApp is derived from CWinThread, a derived application class can hook into the idle loop by replacing CWinApp::OnIdle with an OnIdle function of its own.

Back in the days of Windows 3.x, when applications were inherently single-threaded, OnIdle was the perfect place to perform background processing tasks such as print spooling and garbage collecting. In 32-bit Windows, CWinApp::OnIdle's usefulness is greatly diminished because low-priority tasks can be performed more efficiently in background threads of execution. OnIdle still has legitimate uses, however. MFC uses it to update toolbar buttons and other always-visible user interface objects by calling update handlers registered in the message map. It also takes advantage of times when the application isn't busy processing messages by deleting temporary objects created by functions such as CWnd::FromHandle and CWnd::GetMenu.

When you call FromHandle to convert a window handle into a CWnd pointer, MFC consults an internal table called a handle map that correlates CWnd objects and window handles. If it finds the handle it's looking for, MFC returns a pointer to the corresponding CWnd object. If the window handle doesn't appear in the handle map because a corresponding CWnd doesn't exist, however, FromHandle creates a temporary CWnd object and returns its address to the caller. The next time OnIdle is called (which doesn't occur until after the message handler that called FromHandle returns), MFC cleans up by deleting the temporary CWnd object. That's why the documentation for some MFC functions warns that returned pointers might be temporary and "should not be stored for later use." What that really means is that an object referenced by one of these temporary pointers isn't guaranteed to exist outside the scope of the current message handler because, once that handler returns, OnIdle is liable to be called—and the object deleted—at any moment.

Using OnIdle

An MFC application can enact its own idle-processing regimen by overriding the virtual OnIdle function that it inherits from CWinApp. OnIdle is prototyped as follows:

virtual BOOL OnIdle (LONG lCount)

lCount is a 32-bit value that specifies the number of times OnIdle has been called since the last message was processed. The count continually increases until the message loop in CWinThread::Run calls PumpMessage to retrieve and dispatch another message. The count is then reset to 0 and starts again. WM_PAINT messages, WM_SYSTIMER messages, and certain mouse messages don't cause lCount to be reset. (WM_SYSTIMER is an undocumented message Windows uses internally.) lCount can be used as a rough measure of the time elapsed since the last message or of the length of time the application has been idle. If you have two background tasks you'd like to perform during idle time, one that's high priority and another that's low, you can use lCount to determine when to execute each task. For example, you might perform the high-priority task each time lCount reaches 10 and the low-priority task when lCount hits 100 or even 1,000.

If you could log the calls to an application's OnIdle function without slowing it down, you'd find that 1,000 is not all that high a number. Typically, OnIdle is called 100 or more times per second when the message queue is empty, so a low-priority background task that kicks off when lCount reaches 1,000 is typically executed when the mouse and keyboard are idle for a few seconds. A high-priority task that begins when lCount reaches 10 is executed much more often because the count frequently reaches or exceeds 10, even when the message loop is relatively busy. Idle processing should be carried out as quickly as possible because message traffic is blocked until OnIdle returns.

The value that OnIdle returns determines whether OnIdle will be called again. If OnIdle returns a nonzero value, it's called again if the message queue is still empty. If OnIdle returns 0, however, further calls are suspended until another message finds its way into the message queue and the idle state is reentered after the message is dispatched. The mechanism that makes this work is the bIdle flag in CWinThread::Run, which is initially set to TRUE but is set to FALSE if OnIdle returns FALSE. The while loop that calls OnIdle tests the value of bIdle at the beginning of each iteration and falls through if bIdle is FALSE. bIdle is set to TRUE again when a message shows up in the message queue and PumpMessage is called. As a practical matter, you can save a few CPU cycles by returning FALSE from OnIdle if your background processing is complete for the moment and you don't want OnIdle to be called again until the flow of messages resumes. Be careful, however, not to return FALSE before the framework has finished its most recent spate of idle-processing chores and thus deprive it of the idle time it needs.

The cardinal rule to follow when using OnIdle is to call the base class version of OnIdle from the overridden version. The following OnIdle override demonstrates the proper technique. The base class's OnIdle function is called first, and after the call returns, the application performs its own idle processing:

BOOL CMyApp::OnIdle (LONG lCount)
{
    CWinApp::OnIdle (lCount);
    DoIdleWork (); // Do custom idle processing.
    return TRUE;
}

It turns out that the framework does its processing when lCount is 0 and 1. Therefore, an even better approach is to accord higher priority to the framework's OnIdle handler by delaying the start of your own idle processing until lCount reaches a value of 2 or higher:

BOOL CMyApp::OnIdle (LONG lCount)
{
    CWinApp::OnIdle (lCount);
    if (lCount == 2)
        DoIdleWork (); // Do custom idle processing.
    return TRUE;
}

You can see for yourself what MFC does during idle time by examining the source code for CWinThread::OnIdle in Thrdcore.cpp and CWinApp::OnIdle in Appcore.cpp.

Because the OnIdle implementations in the previous paragraph always returns TRUE, calls to OnIdle will continue unabated even if both you and the framework are finished with OnIdle for the time being. The following OnIdle override reduces overhead by returning FALSE when both MFC's idle processing and the application's idle processing are complete:

BOOL CMyApp::OnIdle (LONG lCount)
{
    BOOL bContinue = CWinApp::OnIdle (lCount);
    if (lCount == 2)
        DoIdleWork (); // Do custom idle processing.
    return (bContinue œœ lCount < 2);
}

The fact that application-specific idle processing isn't started until lCount equals 2 means that the framework won't be deprived of the idle time it needs if the application's OnIdle function returns FALSE.

It's important to perform idle processing as quickly as possible to avoid adversely impacting the application's responsiveness. If necessary, break up large OnIdle tasks into smaller, more manageable pieces and process one piece at a time in successive calls to OnIdle. The following OnIdle function begins its work when lCount reaches 2 and continues responding to OnIdle calls until DoIdleWork returns 0:

BOOL CMyApp::OnIdle (LONG lCount)
{
    BOOL bMFCContinue = CWinApp::OnIdle (lCount);
    BOOL bAppContinue = TRUE;
    if (lCount >= 2)
        bAppContinue = DoIdleWork (); // Do custom idle processing.
    return (bMFCContinue œœ bAppContinue);
}

Because DoIdleWork's return value is also used as OnIdle's return value, OnIdle will cease to be called once DoIdleWork has completed its appointed task.

Idle Processing vs. Multithreading

In Chapter 17, you'll learn about another way to perform background tasks that involves multiple threads of execution. Multithreading is a powerful programming paradigm that's ideal for performing two or more tasks in parallel. It's also scalable: on a multiprocessor system containing n CPUs, Windows NT and Windows 2000 will execute up to n threads concurrently by scheduling each to run on a different processor. (Windows 95 and Windows 98, by contrast, force all threads to share a single CPU, even on multiprocessor systems.)

Given the robust multithreading support in 32-bit Windows, it's reasonable to ask when, if at all, you should use idle processing in lieu of multithreading. Here are two answers:

When you have background tasks to perform that must execute in the application's primary thread. User interface_related tasks tend to be very thread-sensitive. That's one reason why MFC performs user interface updates in the primary thread.

When you have background tasks to perform and the application that you're writing must work in 16-bit Windows as well as in 32-bit Windows. Multithreading is not supported in 16-bit Windows.

In these situations, performing background tasks in OnIdle makes a lot of sense. Under any other circumstances, multithreading is in all likelihood the proper solution.

Python编辑器IDLE使用教程

转自:http://hao360.blog.51cto.com/5820068/1339919 开始->程序->Python 2.*/3.*-> IDLE (Python GUI) ...
  • k_shmily
  • k_shmily
  • 2017年03月13日 15:03
  • 2186

K22中使用UART的IDLE Line功能

当UART接收的数据为不定长度时,那么该如何判断一帧数据的结束呢?我们可以使用IDLE LINE功能来判断。 同事Ji Cheng在其博客http://blog.chinaaet.com/de...
  • wangwenxue1989
  • wangwenxue1989
  • 2015年07月06日 23:52
  • 1113

python之IDLE编辑器功能详细介绍

IDLE是python软件包自带的集成开发环境,可以方便的创建、运行和调试python程序。 启动IDLE后先看到的是python shell,可以通过它在IDLE内部执行python命令。IDLE还...
  • maryhuan
  • maryhuan
  • 2013年11月03日 17:40
  • 3423

Python IDLE 中文乱码问题

玩过 Python 的人都知道,搞 Python 开发一般都是在 Python 自带的 IDLE 集成开发环境中写代码。然而,在开始学习的时候,想要处理中文字符,总是会遇到出现鬼符的情况···...
  • zlxzlxzlxzlxzlx
  • zlxzlxzlxzlxzlx
  • 2015年01月08日 10:17
  • 4064

Ubuntu系统下,Python的使用和idle的安装使用

1.  Python 安装 得到所有 Python相关软件最直接的方法就是去访问它的网站(http://python.org),或者(http://corepython.com)。 Python的安装...
  • bingqilin_
  • bingqilin_
  • 2015年08月05日 11:02
  • 13095

Linux : CPU Idle

CPU Idle状态可以分为很多种Idle状态,在CPU准备进入idle的时候在很多状态中进行选择以达到省电的目的。CPU Idle相关的软件架构可以分以下几种: CPUIDLE core:CPUId...
  • hongzg1982
  • hongzg1982
  • 2017年02月05日 15:38
  • 1648

零基础学python-1.2 什么是idle

(下面是摘自百度百科) IDLE是开发python程序的基本IDE(集成开发环境),具备基本的IDE的功能,是非商业Python开发的不错的选择。当安装好python以后,IDLE就自动安装好了,不需...
  • raylee2007
  • raylee2007
  • 2015年08月09日 16:20
  • 4743

如何修改python IDLE代码及语法主题 配色——拷贝的是别人喜欢的,来学会调试自己喜欢的颜色吧

相信刚进入python学习之路的朋友们,都还是挺喜欢python自带的IDLE,但是白的代码背景色以及其它的代码色确实让人看着有点不舒服,所以当时也琢磨着能不能自己给它换换颜色,这个当然可以,废话不多...
  • Eddy_zheng
  • Eddy_zheng
  • 2015年07月28日 17:05
  • 6826

IDLE设置主题

链接:http://pan.baidu.com/s/1i3HBbLV 密码:120x将下载的Python自带的IDLE配色:打开IDLE -> option -> configure IDLE...
  • Sifastiane
  • Sifastiane
  • 2015年09月22日 16:51
  • 1884

【转】python3.3.3右键菜单Edit with IDLE不能启动,打开IDLE后新建和打开闪退问题

在Idle的源代码里加上句代码自动删除历史记录文件 完事 完美解决 你的python安装目录\lib\idlelib\idle.pyw 在头上加上这么两句代码 import os os.r...
  • qxw88
  • qxw88
  • 2015年06月24日 09:35
  • 2557
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Idle Processing
举报原因:
原因补充:

(最多只允许输入30个字)