Window下的时间计时

秒计时

1.1 使用time()


函数原型:time_t time(time_t * timer)

功能:返回以格林尼治时间(GMT)为标准,从19701100:00:00到现在的此时此刻所经过的秒数。

: localtime, gmtime, asctime, ctime也可以获得当前系统时间或是标准时间.

注意difftime函数可以计算两个time_t类型的时间的差值例如: difftime(t2, t1), 我们也可以t2 - t1, 但是time()返回的值单位不一定是秒所以使用difftime()更可靠。 

time_t start, end, ttt;  
time(&start);
Sleep(1000);
time(&end);
ttt = difftime(end, start);

毫秒计时

2.1 使用GetTickCount


DWORD GetTickCount(void);

定义: 

For Release configurations, this function returns the number of milliseconds since the device booted, excluding any time that the system was suspended. GetTickCount starts at 0 on boot and then counts up from there.

Release版本中,该函数从0开始计时,返回自设备启动后的毫秒数(不含系统暂停时间)。

For Debug configurations, 180 seconds is subtracted from the the number of milliseconds since the device booted. This allows code that uses GetTickCount to be easily tested for correct overflow handling.

Debug版本中,设备启动后便从计时器中减去180秒。这样方便测试使用该函数的代码的正确溢出处理。

 

Return Values

The number of milliseconds indicates success.

返回值:如正确,返回毫秒数。 

unsigned long lBegin, lEnd;
lBegin = ::GetTickCount();
Sleep(1000);
lEnd = ::GetTickCount();
unsigned long l = lEnd - lBegin;

 

2.2 使用clock


clock_t clock(void); 这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数,在MSDN中称之为挂钟时间(wal-clock).

unsigned long  tBegin = ::clock();
Sleep(1000);
unsigned long tEnd = ::clock();
unsigned long ll = (tEnd - tBegin) * 1000 / CLK_TCK;
// 通常CLK_TCK就是1000, 为了效率, tEnd - tBegin就是毫秒数了
// 但是也要看CLK_TCK

 

2.3 使用timeGetTime


函数原型:DWORD timeGetTime(VOID);

功能:返回系统时间,以毫秒为单位。系统时间是从系统启动到调用函数时所经过的毫秒数.

注意这个值是32位的,会在02^32之间循环,约49.71并且精度不是很高可用timeBeginPeriodtimeEndPeriod函数提高timeGetTime函数的精度.

没有精度使用又麻烦而且有其他方法获得毫秒级的计时所以这种方法基本上不使用. 

#include "Mmsystem.h"
#pragma comment(lib,"Winmm.lib")
DWORD dwStart, dwEnd;  
dwStart = timeGetTime();
Sleep(1000);
dwEnd = timeGetTime();
DWORD dw = dwEnd - dwStart;

 

2.4 小结


GetTickCountclock的说明中可以知道:

1. 可以使用GetTickCount计算电脑开机持续时间.

2. 可以使用clock计算某个进程的运行持续时间.

3. timeGetTime需要手动添加lib, 而且精度可能会不好所以甚至可以不使用.


微秒计时

3.1 使用QueryPerformanceFrequency

原型:BOOL QueryPerformanceFrequency(LARGE_INTERGER *lpFrequency);

功能:返回硬件支持的高精度计数器的频率(每秒钟的CPU Tick)

 

原型:BOOL QueryPerformanceCounter(LARGE_INTEGER *lpCount);

功能:返回高精度计数器的计数值

 

所以消逝时间 = (EndTick - BeginTick) / TickPreSecond;   (单位是秒精度可以达到微秒(看你的硬件而定))

LARGE_INTEGER llTemp, llBegin1, llEnd1;
::QueryPerformanceFrequency(&llTemp); // 获取CPU时钟频率 3220820 kHZ
 
::QueryPerformanceCounter(&llBegin1); // 获取开始计数值 54826082774
Sleep(100);
::QueryPerformanceCounter(&llEnd1);     // 获取结束计数值 54826404859
 
double d = (llEnd1.QuadPart - llBegin1.QuadPart) / (llTemp.QuadPart * 1000.0);  // 单位是秒, 精确到微秒

说明

1. 调用QueryPerformanceFrequency时, 我的电脑返回的值是3220820, 很明显, 其单位是kHZ(我的CPU主频是3.2GHZ)

2. 也就是说3220820倒数一下, 精度也只能达到微秒级了.

 

纳秒计时

4.1 使用RDTSC读取时间标签计数器

4.1.1 RDTSC 指令

这是X86架构CPU汇编指令

指令: RDTSC (操作码: 0x0F 0x31)

功能将时间标签计数器读入EDX:EAX寄存器中.

说明Pentium以上的CPU中,提供了一条机器指令RDTSC来读取这个时间戳的数字,并将其保存在EDX:EAX寄存器对中。由于EDX:EAX寄存器对恰好是Win32平台下C++语言保存函数返回值的寄存器所以我们可以把这条指令看成是一个普通的函数调用.

inline unsigned __int64 GetRDTSC()  
{   
    __asm RDTSC
}
// 如果编译器不允许直接用RDTSC的话,可以用_emit伪指令直接嵌入该指令的机器码形式0x0F 0x31:
inline unsigned __int64 GetRDTSC()   
{  
    __asm _emit 0x0F
    __asm _emit 0x31
} 
 

GetRDTSC得到CPU跳了多少次如果我们知道跳一次的用时就可以算出持续时间.

跳一次的用时 = 1 / CPU主频 (单位)

 

4.1.2 计算CPU主频


Windows平台下可以使用QueryPerformanceFrequency()函数获得CPU的主频但是在标准C/C++中没有这样的函数.

你可以间接发计算出主频例如:

unsigned __int64 GetCPUHZ()
{
	unsigned __int64 nBegin = GetRDTSC();
	Sleep(1000);
	unsigned __int64 nEnd = GetRDTSC();
	return (nEnd - nBegin);	
}


说明

1. 这个函数的误差在于Sleep(1000)的执行时间不是1可能是1.0002.

2. QueryPerformanceFrequency得到的主频的单位是 kHZ, 也就是说其精度只能达到微秒而不是纳秒.

  

4.1.3 计算持续时间

unsigned __int64 nHZ = GetCPUHZ();
unsigned __int64 nBegin = GetRDTSC();
...
unsigned __int64 nEnd = GetRDTSC();
double dTime = (nEnd - nBegin) * 1.0 / nHZ;// 单位是秒, 精度甚至可以达到纳秒

 

说明:

1. 这里的精度可以达到纳秒但是在纳秒级可能不是很准因为GetCPUHZ() 得到主频有误差.

2. 纳秒计时的粒度最小所以你也可以使用这种方法进行微秒毫秒秒的计时.

 

 

小结

1. 计时与定时不同这里计时可以达到纳秒级但是定时就不能(因为执行一条指令可能至少要100纳秒).

 

### 计时器在Windows中的实现方法 #### 非窗口类中的计时器实现 对于非窗口类的应用场景,可以通过 `QObject` 的成员函数 `startTimer()` 来实现定时器功能。此函数会启动一个计时器并返回计时器标识符[^3]。每次计时器触发时,都会调用对象的 `timerEvent(QTimerEvent *event)` 虚拟函数。开发者需要重写这个函数以处理具体的逻辑。 以下是基于 Qt 框架的一个简单示例: ```cpp #include <QObject> #include <QDebug> class MyObject : public QObject { Q_OBJECT protected: void timerEvent(QTimerEvent *event) override { if (event->timerId() == m_timerId) { qDebug() << "Timer event triggered!"; } } public: explicit MyObject(QObject *parent = nullptr) : QObject(parent), m_timerId(-1) {} void startCustomTimer(int interval) { m_timerId = this->startTimer(interval); } private: int m_timerId; }; int main() { MyObject obj; obj.startCustomTimer(1000); // 设置每触发一次 } ``` 上述代码展示了如何通过继承 `QObject` 并覆盖其虚函数 `timerEvent` 实现自定义行为。 --- #### 窗口类中的计时器实现 在传统的 Windows API 中,可以使用 `SetTimer` 函数来创建计时器。它允许指定时间间隔以及关联的窗口句柄,从而使得计时器到期后能够向目标窗口发送消息 `WM_TIMER`[^4]。这种机制非常适合于 GUI 应用程序开发。 下面是一个简单的 C++ 示例展示如何利用 WinAPI 处理计时器事件: ```cpp LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static int nCount = 0; switch (message) { case WM_TIMER: ++nCount; OutputDebugStringA(("Timer ticked! Count: " + std::to_string(nCount)).c_str()); break; case WM_DESTROY: KillTimer(hWnd, IDT_TIMER1); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { const char* CLASS_NAME = "Sample Window Class"; WNDCLASS wc = {}; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); HWND hwnd = CreateWindowEx( 0, CLASS_NAME, "Learn to Program Windows", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); ShowWindow(hwnd, nCmdShow); SetTimer(hwnd, IDT_TIMER1, 1000, NULL); // 启动计时器,每隔一触发一次 MSG msg = {}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } UnregisterClass(CLASS_NAME, hInstance); return 0; } ``` 在此例子中,`SetTimer` 被用来注册一个周期性的计时器,并且每当计时器被触发时,会在调试控制台打印当前计数。 --- #### C# 中的计时器实现 在 .NET Framework 下构建 Windows Forms 应用程序时,通常会选择内置的 `System.Windows.Forms.Timer` 类型作为解决方案之一。此类提供了非常简便的方式去管理 UI 更新或其他后台操作的任务调度[^2]。 以下是一段典型的 C# 定时器初始化与绑定到按钮点击事件的例子: ```csharp using System; using System.Timers; using System.Windows.Forms; namespace TimerExampleApp { public partial class MainForm : Form { private readonly Timer _myTimer; public MainForm() { InitializeComponent(); _myTimer = new Timer { Interval = 1000 }; // 设定为每一钟触发一次 _myTimer.Elapsed += OnTimedEvent; // 绑定回调函数 } protected virtual void OnTimedEvent(Object source, ElapsedEventArgs e) { Invoke((MethodInvoker)(() => labelStatus.Text = DateTime.Now.ToString())); } private void btnStart_Click(object sender, EventArgs e) { _myTimer.Start(); // 开始计时器 } private void btnStop_Click(object sender, EventArgs e) { _myTimer.Stop(); // 停止计时器 } } } ``` 在这个案例里,当用户按下 “开始” 和 “停止” 按钮的时候分别启停了一个系统级别的高精度计时器实例[^5]。 --- #### 总结 无论是采用原生 WinAPI 还是现代跨平台框架如 Qt 或者 Microsoft 提供的语言支持库(比如 C#.Net),都可以方便快捷地完成各种复杂程度不同的计时需求。具体选择取决于项目的技术栈和个人偏好等因素决定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值