Windows精确时间获取方法

Windows精确时间获取方法

很多时候,我们需要计算两个运行过程间计算机需要耗时多少,一般情况下我们采用GetTickcount函数:它返回从操作系统启动到当前所经过的毫秒数,常常用来判断某个方法执行的时间,其函数原型是DWORD GetTickCount(void),返回值以32位的双字类型DWORD存储,因此可以存储的最大值是(2^32-1) ms约为49.71天,因此若系统运行时间超过49.71天时,这个数就会归0,MSDN中也明确的提到了:“Retrieves the number of milliseconds that have elapsed since the system was started, up to 49.7 days.”。因此,如果是编写服务器端程序,此处一定要万分注意,避免引起意外的状况(如需避免此种情况可使用Ctime类或者是系统API的SYSTEMTIME进行判断),但是这个函数并非实时发送,而是由系统每18ms发送一次,因此其最小精度为18ms。当需要有小于18ms的精度计算时,应使用StopWatch方法进行。

经过不断测试发现:

  1. 连续触发200次,实测下来,最小间隔在15ms。

  2. 实际状况应该是系统每秒触发64次,间隔在15、16ms之间波动

    为了得到精确的时间我们采QueryPerformanceCounter这样的API,而QueryPerformanceCounter的使用有一些隐含的陷阱需要注意。

    关于QueryPerformanceCounter
    官方解释:https://msdn.microsoft.com/zh-cn/ms644904,用于高精度计时器时间读取,重点是执行成功返回非0值,精度是<1us的。

    多核CPU采用QueryPerformanceCounter的问题
    程序中通常使用时间差定时地调用某种接口的情况,而时间差的使用有一个隐含因素,即时间是顺序累加的,当然我们通常的时间当然是累加的,不会出现停滞甚至倒转,而QueryPerformanceCounter的运行情况是依赖于CPU的,当CPU是多核时,在某一线程内调用QueryPerformanceCounter,线程会切换于不同的核心之间,这时候QueryPerformanceCounter返回值是不确定的,或者说这时候的计时器并不能保证是顺序累加的,相应地,当使用时间差时会出现负数或者0的情况,这显然不符合开发者的预期。

下面是一个采用QueryPerformanceCounter函数编写的类,可以用于多线程(实测,我已用于很多项目中):

TimerControl.h


```cpp
#pragma once

#define WM_USER_TIMER_PULSE                    WM_USER + 0x101

//产生自定义时长脉冲信号,以毫秒为单位
UINT ThreadCustomMilliSecondTime(LPVOID pParam);

class CTimerControl
{
public:
    CTimerControl(void);
    ~CTimerControl(void);

    //得到当前系统时间
    double GetCurrTime(void);

    //按指定时间延时,以毫秒为单位
    void static DelaySpecTime(int specTime);

    //控制是否发生脉冲信号
    void SetSignalSend(bool isSend = true);

    //停止脉冲信号源
    void StopTimerThread();

    //开始自定义脉冲线程
    void BeginTimerThread(HWND hWnd);

    //设定自定义脉冲时间
    void SetTimerSpace(float customTime);
    // 得到当前点的时间
    double GetCurPotTimer(void);
};``

TimerControl.cpp文件

#include "StdAfx.h"
#include "TimerControl.h"
#include ".\timercontrol.h"

bool m_isContinue;
bool m_isSendMessage;
float m_timeSpace;
double m_curPotTimer;
LARGE_INTEGER m_iFrequency;

//产生自定义时长脉冲信号,以毫秒为单位
UINT ThreadTime(LPVOID pParam)
{
	CTimerControl timerCtrl;
	HWND hWnd = (HWND)pParam;
    LARGE_INTEGER iCounter;
	LONGLONG preTime;
	LONGLONG aftTime;
	double realTime;

	QueryPerformanceCounter(&iCounter);
	preTime = iCounter.QuadPart;
	while(m_isContinue)
	{
		QueryPerformanceCounter(&iCounter);
		aftTime = iCounter.QuadPart;
		realTime = ((double)(aftTime - preTime) / (double)m_iFrequency.QuadPart) * 10000000;
		m_curPotTimer = (double)iCounter.QuadPart / (double)m_iFrequency.QuadPart;
        if((int)(realTime + 0.5) >= m_timeSpace)
		{
			preTime = aftTime;
			if (m_isSendMessage)
			    PostMessage(hWnd, WM_USER_TIMER_PULSE, 0, 0);
		}
	}
	return 0;
}

CTimerControl::CTimerControl(void)
{
    QueryPerformanceFrequency(&m_iFrequency);	
}

CTimerControl::~CTimerControl(void)
{
	m_timeSpace = 9999999;
	m_isSendMessage = false;
	m_isContinue = false;
}

//得到当前系统时间
double CTimerControl::GetCurrTime(void)
{
	double currTime;
    LARGE_INTEGER iCounter;

	QueryPerformanceCounter(&iCounter);
	currTime = (double)iCounter.QuadPart / (double)m_iFrequency.QuadPart;

	return currTime;
}

//按指定时间延时,以毫秒为单位
void CTimerControl::DelaySpecTime(int specTime)
{
    LARGE_INTEGER iCounter, iFrequency;
	LONGLONG preTime;
	LONGLONG aftTime;
	double realTime;

    QueryPerformanceFrequency(&iFrequency);
	QueryPerformanceCounter(&iCounter);
	preTime = iCounter.QuadPart;
	while(true)
	{
		QueryPerformanceCounter(&iCounter);
		aftTime = iCounter.QuadPart;
		realTime = ((double)(aftTime - preTime) / (double)iFrequency.QuadPart) * 10000000;
        if((int)(realTime + 0.5) >= specTime * 9990)
			return;
	}
}

//控制是否发生脉冲信号
void CTimerControl::SetSignalSend(bool isSend)
{
	m_isSendMessage = isSend;
}

//停止脉冲信号源
void CTimerControl::StopTimerThread()
{
	m_isSendMessage = false;
	m_isContinue = false;
	DelaySpecTime(200);
}

//开始时间脉冲线程
void CTimerControl::BeginTimerThread(HWND hWnd)
{
	m_isContinue = true;
	m_isSendMessage = false;
	AfxBeginThread(ThreadTime, hWnd);
}

//设定自定义脉冲时间
void CTimerControl::SetTimerSpace(float customTime)
{
	//m_timeSpace = customTime * 9992  * (float)0.65;
	m_timeSpace = customTime * 9992;
}

// 得到当前点的时间
double CTimerControl::GetCurPotTimer(void)
{
	::GetTickCount();
	return m_curPotTimer;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值