在 C#開發(fā)過(guò)程中,經(jīng)常需要用到定時(shí)器,如果只是為了開啟一個(gè)線程,或者異步的做一些事情倒還好,直接使用 Form 或者 Thread 的 Timer 功能即可。
但是如果需求是比較精確的計(jì)時(shí)功能,這個(gè) Timer 就極不好用了,因?yàn)樗麄兪蔷€程上的定時(shí)器,會(huì)受到系統(tǒng)調(diào)度的干擾,精度非常差,CPU 使用率高一點(diǎn)點(diǎn)就會(huì)影響計(jì)時(shí)精度。
常用的 Windows API 方法 GetTickCount() 返回系統(tǒng)啟動(dòng)后經(jīng)過(guò)的毫秒數(shù)。另一方面,GetTickCount() 函數(shù)僅有 1ms 的分辨精度,精度也很不好。
我們要另外尋找一種方法來(lái)精確測(cè)量時(shí)間。
Win32 API 使用 QueryPerformanceCounter() 和 QueryPerformanceFrequency() 方法支持高精度計(jì)時(shí)。這些方法,比“標(biāo)準(zhǔn)的”毫秒精度的計(jì)時(shí)方法如 GetTickCount() 之類有高得多的精度。
雖然在 C# 中使用“非托管”的 API 函數(shù)會(huì)有一定的開銷,但比起使用一點(diǎn)都不精確的 GetTickCount() API 函數(shù)來(lái)說(shuō)要好得多了。
下面的類實(shí)現(xiàn)了 QueryPerformanceCounter() 和 QueryPerformanceFrequency() API 函數(shù)功能的封裝。
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Threading;
namespace Win32
{
internal class HighTimer
{
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceFrequency(out long lpFrequency);
private long startTime, stopTime;
private long freq;
// 構(gòu)造函數(shù)
public HighTimer()
{
startTime = 0;
stopTime = 0;
if (QueryPerformanceFrequency(out freq) == false)
{
// 不支持高性能計(jì)數(shù)器
throw new Win32Exception();
}
}
// 開始計(jì)時(shí)器
public void Start()
{
// 來(lái)讓等待線程工作
Thread.Sleep(0);
QueryPerformanceCounter(out startTime);
}
// 停止計(jì)時(shí)器
public void Stop()
{
QueryPerformanceCounter(out stopTime);
}
// 返回計(jì)時(shí)器經(jīng)過(guò)時(shí)間(單位:秒)
public double Duration
{
get
{
return (double)(stopTime - startTime) / (double)freq;
}
}
}
}
上面封裝類的使用方法:
HighTimer pt = new HighTimer(); // 創(chuàng)建新的 HighTimer 對(duì)象
pt.Start(); // 啟動(dòng)計(jì)時(shí)器
Console.WriteLine("Test/n"); // 需要計(jì)時(shí)的代碼
pt.Stop(); // 停止計(jì)時(shí)器
Console.WriteLine("Duration: {0} sec/n", pt.Duration); // 打印需要計(jì)時(shí)部分代碼的用時(shí)