C#中精确计时的一点收获

jintianhu2000 2010-09-01 06:07:21
以下所有代码运行环境: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来设置精度

[DllImport("winmm")]
static extern void timeBeginPeriod(int t);
[DllImport("winmm")]
static extern void timeEndPeriod(int t);

缺点:与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。

[DllImport("kernel32")]
static extern bool QueryPerformanceFrequency(ref long PerformanceFrequency);

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 使用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毫秒,大部分境况应该都够用了。偶尔的场合可以考虑用QueryPerformanceCounter

...全文
366 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
ysz89757 2010-09-02
  • 打赏
  • 举报
回复
学习了
bloodish 2010-09-01
  • 打赏
  • 举报
回复
楼主好学,可是Stopwatch有你要的全部,精确到Tick级别的计时
onenewsmile 2010-09-01
  • 打赏
  • 举报
回复
我感觉这些方法效果都是差别不大的,软计时本来就不能要求太高
-过客- 2010-09-01
  • 打赏
  • 举报
回复
.NET中不是已经提供了Stopwatch类了吗
hjx398 2010-09-01
  • 打赏
  • 举报
回复
学习下
wuyq11 2010-09-01
  • 打赏
  • 举报
回复
总结的不错
无论用什么方法,误差总有的,10毫秒内的误差无法控制
studentliudong 2010-09-01
  • 打赏
  • 举报
回复
学习下
johndii 2010-09-01
  • 打赏
  • 举报
回复
看了感觉很透彻的,但结果还是用timeGetTime就好,让我很沮伤。。。。

以我工作的领域没必要ms以上了。。。
兔子-顾问 2010-09-01
  • 打赏
  • 举报
回复
弄来弄去效果都一样。
无外乎获取当前时间或是启动系统到当前的时间戳。
.net下用Environment.TickCount就可以了,不需要GetTickCount这个api的调用。
当然,你总结的方法都可以用。
龍月 2010-09-01
  • 打赏
  • 举报
回复
沙发!!!!!!!!!!!!!!!

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧