现在的位置: 首页 > 综合 > 正文

白话windows多线程同步之可等待计时器内核对象

2019年03月10日 ⁄ 综合 ⁄ 共 2116字 ⁄ 字号 评论关闭

    引子:老王有一个果园。春天来了,老王每天都要隔段时间给果树浇水,修枝,除虫,或者其他工序,有些工序是有时间间隔的,不能同时进行这些工序。老王请小张写一个程序,帮他管理他的果园。小张在网上搜索了一番,发现计时器可以用来定时做某些事,但是无法控制他们的同步,于是他找我求救,我给他写了下面的文章。

可等待计时器内核对象既可以定时来做某些事,也可以控制他们的同步。下面给出代码:

#include "stdafx.h"
#include <Windows.h>
#include <process.h>

HANDLE g_hWaitTimer = NULL ;

DWORD WINAPI Fun(LPVOID lp)
{
	WaitForSingleObject(g_hWaitTimer, INFINITE) ;
	printf("%d doing something !!!\n", GetCurrentThreadId()) ;
	return 0 ;
}

int g_time = 0 ;
void CALLBACK TimerProc(HWND hwnd, UINT msg, UINT_PTR id, DWORD dwTime)
{
	if(g_time < 5)
		printf("%d seconds has elapsed!!!\n", ++g_time) ;
}

int _tmain(int argc, _TCHAR* argv[])
{
	g_hWaitTimer = CreateWaitableTimer(NULL, TRUE, NULL) ;
	const int nTimerUnitsPerSecond = 10000000;
	LARGE_INTEGER li ;
	li.QuadPart = -(5 * nTimerUnitsPerSecond) ;
	SetWaitableTimer(g_hWaitTimer, &li, 3 * 1000, NULL, NULL, FALSE) ;

	for(int i = 0 ; i < 6 ; i++){
		CreateThread(NULL, 0, Fun, 0, 0, NULL) ;
	}
	int ret = SetTimer(NULL, 0, 1000, TimerProc) ;

	MSG msg ;
	while(GetMessage(&msg, NULL, 0, 0))	{
		if(msg.message == WM_TIMER){
			DispatchMessage(&msg) ;
		}
	}
	return 0;
}

下面给出分析:

CreateWaitableTimer用来创建等待定时器,第一个参数和第三个参数暂且忽略不讲,都是内核安全性等问题,第二个参数表明是不是人工重置定时器(关于人工重置与自动重置的区别,请参看该系列博文:windows多线程之事件对象 http://blog.csdn.net/zxm342698145/article/details/27095043)。然后调用SetWaitableTimer设置一个定时器。该函数定义如下:

BOOL SetWaitableTimer(
   HANDLE hTimer,
   const LARGE_INTEGER *pDueTime,
   LONG lPeriod,
   PTIMERAPCROUTINE pfnCompletionRoutine,
   PVOID pvArgToCompletionRoutine,
   BOOL fResume);

这个函数带有若干个参数,使用时很容易搞混。显然, h Ti m e r参数用于指明你要设置的定时器。p D u e Ti m e和l P e r i o d两个参数是一道使用的。P D u e Ti m e r参数用于指明定时器何时应该第一次报时,而l P e r i o d参数则用于指明此后定时器应该间隔多长时间报时一次。具体的含义请参看MSDN,再次声明,咋写博客只是记录并描述该技术的应用场合和技巧,不探讨函数的方方面面。

如果不设置定时器应该第一次报时的绝对时间,也可以让定时器在一个相对于调用S e t Wa i t a b l e Ti m e r的时间进行报时。只需要在p D u e Ti m e参数中传递一个负值。传递的值必须是以1 0 0 n s为间隔。由于我们通常并不以1 0 0 n s的间隔来思考问题,因此我们要说明一下1 0 0 n s的具体概念:1
s = 1 0 0 0 m s = 1 0 0 0 0 0 0 µ s = 1 0 0 0 0 0 0 0 0 0 n s 。

请看程序运行效果:

设置的时间5s一到各个线程就开始工作,多么优雅,嘎嘎。对了,刚才所说的,由于某些工序不能同时进行,要让这些工序挨个进行。这也好办,创建自动可等待计时器内核对象就好,即第二个参数为FALSE。

最后要说明的是,书上说,凡是称职的Wi n d o w s编程员都会将等待定时器与用户定时器(用S e t Ti m e r函数进行设置)进行比较,哈哈,我是称职的程序员,所以还是将他们比较一番。它们之间的最大差别是,用户定时器需要在应用程序中设置许多附加的用户界面结构,这使定时器变得资源更加密集。另外,等待定时器属于内核对象,这意味着它们可以供多个线程共享,并且是安全的。

用户定时器能够生成W M _ T I M E R消息,这些消息将返回给调用S e t Ti m e r(用于回调定时器)的线程和创建窗口(用于基于窗口的定时器)的线程。因此,当用户定时器报时的时候,只有一个线程得到通知。另一方面,多个线程可以在等待定时器上进行等待,如果定时器是个人工重置的定时器,则可以调度若干个线程。

抱歉!评论已关闭.