VC中加入定时机制的几种方法

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 11:04   260   0
    定时机制是指在程序运行当中间隔特定的时间引发指定的事件。在DOS下编程时,主要依靠时钟中断Int 8及其调用中断 Int 1cH来实现,应用程序通过修改这些系统中断来达到实现定时触发。而在Windows下,若想象在DOS下肆无忌惮的修改系统是不现实的,那么应当如何实现定时机制呢?下面在下就在学习当中的几点体会谈谈这个问题,提出几种方案供大家参考。
第一种方案是大家熟悉的截获定时消息的途径。
在Windows提供给我们使用的系统资源当中,有一种称为“定时器(Timer)”的特殊资源,在申请了这类资源的程序当中每间隔一段时间会接收到值为WM_TIMER的消息。需要定时执行的代码可以放在该消息的处理部分。如果在VC中,我们可以具体按照以下步骤实现这一目的: 
1.            利用MFC AppWizard创建一个标准的工程,接受所有缺省选项。名为s1 
2.            在Classview中选中“CMainFrame”类,然后按Ctrl+W激活ClassWizard,在“Message Map”选项卡中Class Name选“CMainFrame”,接着在“Message”中选“WM_TIMER”,最后按下“Add Funcation”。以上步骤加入了对WM_TIMER消息的映射处理。 
3.            回到Classview中,双击“OnCreate”成员函数,在函数的末尾添加申请Timer的语句:
申请一个标识值为100的Timer,定时间隔为1000毫秒(1秒)。
4.            在“Classview”中双击OnTimer函数,输入要定时实现的代码。本例子中为:
MessageBeep(1000);;//每隔一秒发出通告声
5.            编译并执行之,我们可以每隔一秒就听到声音。这正是我们在OnTimer函数内要求执行的。 
实际当中,我们可以将“MessageBeep(1000);”换成任何我们想完成的任务,譬如定时存盘等。 
 
第二种方案也利用Timer资源,但却是采用已经编写好的代码;
我们可以加入一个具有定时功能的组件至当前工程当中。这种方法特别适用于基于对话框的工程。具体步骤如下: 
1.            利用MFC AppWizard创建一个基于对话框的工程,其余接受所有缺省选项。名为s2。 
2.            在ResourceView中,双击IDD_S2_DIALOG,显示对话框,将其中的“To do:”改为“定时触发演示的例子”,表明工程的作用。 
3.            右击对话框编辑区,在弹出的右键菜单中选择“Insert ActiveX Control”,从弹出的列表框中选择“Timer Object”,确定后会在对话框内出现一个Timer对象。 
4.            我们右击Timer对象,从弹出的菜单中选择“Properties”,接着选“All”选项卡,将其中的Interval值设为5000,即每隔5秒发生一次Timer事件。 
5.            回到对话框编辑界面,双击Timer,产生一个CS2Dlg::OnTimerTimer1成员函数,接受缺省值,并在函数实现部分输入:
MessageBox("定时触发消息框","定时演示" ,MB_OK);
6.            编译并运行此工程,将会在产生的对话框运行期间,每隔5秒弹出一个消息框。 
同样,我们可以以任何自己的代码来替换5中的消息框语句。详细见附例s2。 
7.            第三种方法是采用线程技术。众所周知,Windows 9X是一个基于多线程的多任务操作系统,在内核中以线程作为调度的基本单位,由系统分时间片进行调度。利用这一点,我们可以在程序当中创建一个“司职”计时的线程,通过线程间的同步来定时触发我们要完成的任务的代码。不象前两种方法需要至少有一个窗口作为接受消息的主窗口,采用线程技术实现定时触发将免去创建窗口的麻烦以及带来的系统各种资源的消耗。下面我们来举一个例子来说明这个问题:我们在CmyApp类的Initstance成员中不建立主窗口而是创建一个工作线程,该线程休眠一定的时间后,自动调用主线程的SomeThing函数。为了支持线程的运行,我们需要给CmyApp类增加相应的线程函数。下面,我们还是一步一步的实现: 
8.            利用MFC AppWizard创建一个标准工程,其中为不产生多余的代码,不选文档/视图支持,并选择单文档。工程名为S3。 
9.            在CS3App:: InitInstance()中用“/* … */”注释掉“return TRUE;”之前的所有代码。这是为了不建立窗口。并添加以下代码:
ExitFlag=TRUE;//是否结束主线程的循环的标志变量。因为子线程严重依赖主线程,所以在本例子中为了避免没有主窗口而提前结束应用程序,从而使子线程无法存在,所以给主线程一个循环,知道全局变量ExitFlag在子线程退出前被设置成FALSE为止.
StartThread();//启动线程

do{}while(ExitFlag);//直到结束子线程
::MessageBox(NULL,"主线程结束!","定时触发演示",MB_OK);
return TRUE;
10.        在Globals中增加一标志变量“ExitFlag”,类型为BOOL。它被主线程用来判断是否结束自身运行。 
11.        通过ClassView在CS3App的Public部分声明以下函数:
void StartThread(void); //启动线程

static UINT ThreadFunction(void); //主要执行代码的函数
static UINT StaticThreadFunc(LPVOID lpparam);//设置线程时用到的函数
需要特别指出的是,用AfxBeginThread进行线程设置时,第一参数必须象本例所指出的那样声明为Static ,不然参数转换的错误会扰得你不得安宁。
12.        在StartThread中输入如下代码:
AfxBeginThread(StaticThreadFunc,this);//建立并启动线程
13.        在StaticThreadFunc中输入如下代码:
return ThreadFunc();//调用完成主要线程代码的函数,注意一定要是Static. 实现
ThreadFunction
int i;
i=5;//触发5
while(i--)
{
Sleep(5000);//间隔5
::MessageBox (NULL,"我被定时触发了!","定时触发演示",MB_OK);
}
ExitFlag=FALSE;//ExitFlag是一全局变量,通知主线程结束运行。
return 0;
}
     编译并运行工程,将看不到应用程序窗口,但可以看到每隔5秒,桌面上出现一个消息框,5次后弹出主线程结束的消息框。 
 
第三种方案是利用多媒体定时器
多媒体定时器(Multimedia Timer)。它使用自己单独的线程(Thread),来调用一个自己的回调函数(Callback Function)。它的优先级很高,它每隔一定时间就发送一个消息而不管其它消息是否执行完。此外,对于现在的Intel CPU来说,它的最小定时精度通常都可以达到1毫秒,足够满足实时数据采集的定时精度。
多媒体定时器可直接用Component Gallery在项目中插入Windows Multimedia组件,此时多媒体定时器所需的头文件和库将自动插入工程的Stdafx.h中,或用手直接将以下语句添入Stdafx.h,即
#include
// CG: The following line was added by the Windows Multimedia component.
#pragma comment(lib, "winmm.lib")
1. 定义定时器参数
#define TEN_MILLI_SECOND 200 //定时器间隔
#define TIMER_ACCURACY 1 //定时器精度
UINTTimer_ID; //定时器句柄
UINTwAccuracy;        //定时器精度参数
2. 通过多媒体定时器设备函数timeGetDeviceCaps获得本微机的最大分辨率。
TIMECAPS tc; //定时器分辨率的结构
If(timeGetDeviceCaps(&tc,sizeof(TIMECAPS))
= = TIMERR_NOERROR)
{
//获得本系统的最小定时器分辨率,所有应用必须大于等于该分辨率
wAccuracy=min(max(tc.wPeriodMin,
TIMER_ACCURACY),tc.wPeriodMax);
//设定本应用的所需的定时器分辨率,本例为微机的所允许的最大分辨率
timeBeginPeriod(wAccuracy);
}
3. 应用多媒体定时器的timeSetEvent函数设定事件的触发方式,它的函数原形是:
MMRESULT timeSetEvent( UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
DWORD dwUser, UINT fuEvent);
uDelay 用于设定事件触发间隔;
uResolution 用于设定程序所需的最小分辨率;
lpTimeProc 调用回调函数;
dwUser 用户提供的回调数据;
fuEvent 事件触发方式,在Visual C++中有两种方式:
TIME_ONESHOT:事件仅触发一次
TIME_PERIODIC:每隔一定时间触发一次
TimeSetEvent函数返回定时器句柄;
具体应用:
Timer_ID=timeSetEvent(TEN_MILLI_SECOND,wAccuracy,
( LPTIMECALLBACK)CatchMMTimer,
(DWORD)hWnd,TIME_PERIODIC);
4. 声明一个全局的回调(Callback)函数
void CALLBACK TwoHundredMilliSecondProc (UINT wTimerID,UINT nMsg,DWORD dwUser,DWORD dw1,DWORD dw2),在回调函数中调用事件触发消息且在回调函数中语句尽量简单,不要在回调函数内做一些耗时的操作;
5. 添加用户消息CatchMMTimer函数,用来接收多媒体定时器的事件通知。其过程是首先在类的头文件定义:#define MYMSG_TIMER WM_USER+101,然后在类头文件的AFX_MSG块中说明消息处理函数:afx_msg LRESULT OnMymsgTimer(WPARAM wParam, LPARAM lParam); 在类实现的消息映射块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数中:ON_MESSAGE (MYMSG_TIMER, OnMymsgTimer)。最后在相应类中实现消息处理函数。关于用户自定义消息具体可参考Visual C++ s书籍。如:PostMessage((HWND)dwUser,MYMSG_TIMER,0,0); //PostMessage发送消息
6. 定时器的任务完成后,要及时删除,否则占用太多内存,系统会越来越慢。删除定时器分两步,首先调用 timeKillEvent函数删除定时器句柄,然后用timeEndPeriod函数删除定时器的分辨率。具体应用如下:
timeKillEvent(Timer_ID);
timeEndPeriod(wAccuracy);
SetTimer(100,1000,NULL);//
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP