原文地址 http://www.cnblogs.com/jintianhu/archive/2010/09/01/1815031.html
以下所有代碼運行環境:Windows 2003, Intel(R) Core(TM) 2 Duo CPU E8400 @ 3.00GHz 2.99GHz,2.96GB內存
根據綜合網上的一些文章,精確計時主要有以下幾種方式
1 調用WIN API中的GetTickCount
[DllImport("kernel32")] static extern uint GetTickCount();
從操作系統啓動到現在所經過的毫秒數,精度爲1毫秒,經簡單測試發現其實誤差在大約在15ms左右
缺點:返回值是uint,最大值是2的32次方,因此如果服務器連續開機大約49天以後,該方法取得的返回值會歸零
用法:
uint s1 = GetTickCount();
Thread.Sleep(2719);
Console.WriteLine(GetTickCount() - s1); //單位毫秒
2 調用WIN API中的timeGetTime 推薦
[DllImport("winmm")] static extern uint timeGetTime();
常用於多媒體定時器中,與GetTickCount類似,也是返回操作系統啓動到現在所經過的毫秒數,精度爲1毫秒。
一般默認的精度不止1毫秒(不同操作系統有所不同),需要調用timeBeginPeriod與timeEndPeriod來設置精度
缺點:與GetTickCount一樣,受返回值的最大位數限制。
用法:
timeBeginPeriod(1);
uint start = timeGetTime();
Thread.Sleep(2719);
Console.WriteLine(timeGetTime() - start); //單位毫秒
timeEndPeriod(1);
3 調用.net自帶的方法System.Environment.TickCount
獲取系統啓動後經過的毫秒數。經反編譯猜測它可能也是調用的GetTickCount,但是它的返回值是int,而GetTickCount與timeGetTime方法的原型中返回值是DWORD,對應C#中的uint,難道.NET對System.Environment.TickCount另外還做了什麼處理麼?
缺點:與GetTickCount一樣,受返回值的最大位數限制。
用法:
int aa = System.Environment.TickCount;
Thread.Sleep(2719);
Console.WriteLine(System.Environment.TickCount - aa); //單位毫秒
注:經過測試,發現GetTickCount、System.Environment.TickCount也可以用timeBeginPeriod與timeEndPeriod來設置精度,最高可將精度提高到1毫秒。不知是什麼原因?
4 調用WIN API中的QueryPerformanceCounter
[DllImport("kernel32.dll ")] static extern bool QueryPerformanceCounter(ref long lpPerformanceCount);
用於得到高精度計時器(如果存在這樣的計時器)的值。微軟對這個API解釋就是每秒鐘某個計數器增長的數值。
如果安裝的硬件不支持高精度計時器,函數將返回false需要配合另一個API函數QueryPerformanceFrequency。
QueryPerformanceFrequency返回硬件支持的高精度計數器的頻率,如果安裝的硬件不支持高精度計時器,函數將返回false。
用法:
long a = 0;
QueryPerformanceFrequency(ref a);
long b = 0, c = 0;
QueryPerformanceCounter(ref b);
Thread.Sleep(2719);
QueryPerformanceCounter(ref c);
Console.WriteLine((c - b) / (decimal)a); //單位秒
精度爲百萬分之一秒。而且由於是long型,所以不存在上面幾個API位數不夠的問題。
缺點:在一篇文章看到,該API在節能模式的時候結果偏慢,超頻模式的時候又偏快,而且用電池和接電源的時候效果還不一樣(筆記本)
原文地址:http://delphi.xcjc.net/viewthread.php?tid=1570
未經過超頻等測試,如果是真的,那該API出來的結果就可能不準。
5 使用.net的System.Diagnostics.Stopwatch類 推薦
Stopwatch 在基礎計時器機制中對計時器的刻度進行計數,從而測量運行時間。如果安裝的硬件和操作系統支持高分辨率性能的計數器,則 Stopwatch 類將使用該計數器來測量運行時間;否則,Stopwatch 類將使用系統計數器來測量運行時間。使用 Frequency 和 IsHighResolution 兩個靜態字段可以確定實現 Stopwatch 計時的精度和分辨率。
實際上它裏面就是將QueryPerformanceCounter、QueryPerformanceFrequency兩個WIN API封裝了一下,如果硬件支持高精度,就調用QueryPerformanceCounter,如果不支持就用DateTime.Ticks來計算。
用法:
Stopwatch sw = new Stopwatch();
sw.Start();
Thread.Sleep(2719);
sw.Stop();
Console.WriteLine(sw.ElapsedTicks / (decimal)Stopwatch.Frequency);
6 使用CPU時間戳進行更高精度計時
原文地址:http://www.chinaunix.net/jh/23/110190.html
該方法的原理我不是很明白,硬件知識太匱乏了。精度是ns
在C#中要用該方法必須先建立一個託管C++項目(因爲要內嵌彙編),編譯成DLL供c#調用,有點麻煩。
C++代碼:
// MLTimerDot.h
#pragma once
using namespace System;
namespace MLTimerDot {
//得到計算機啓動到現在的時鐘週期
unsigned __int64 GetCycleCount(void)
{
_asm _emit 0x0F
_asm _emit 0x31
}
//聲明 .NET 類
public __gc class MLTimer
{
public:
MLTimer(void)
{
}
//計算時鐘週期
UInt64 GetCount(void)
{
return GetCycleCount();
}
};
}
C#調用:
long a = 0;
QueryPerformanceFrequency(ref a);
MLTimerDot.MLTimer timer = new MLTimerDot.MLTimer();
ulong ss= timer.GetCount();
Thread.Sleep(2719);
Console.WriteLine((timer.GetCount() - ss) / (decimal)a);
缺點:和QueryPerformanceCounter一樣,結果不太穩定。
我的結論:常規應用下timeGetTime完全夠用了,將精度調到1毫秒,大部分境況都夠用。System.Diagnostics.Stopwatch由於調用方便,也推薦使用。