当前位置: 首页 > 编程语言 > C#

System.Threading.TimervsSystem.Threading.Thread.Sleep解析-.NETTimer不使用系统时钟解析分享

时间:2023-04-11 01:16:53 C#

System.Threading.TimervsSystem.Threading.Thread.Sleep解析-.NETTimer不使用系统时钟分辨率问题:为什么System.Threading.Timer保持15ms的分辨率,尽管操作系统时钟分辨率更精确?在没有繁忙的CPU等待的情况下实现1ms定时事件解析的可行方法是什么?再次:在我的例子中,系统计时器的分辨率为1ms(与建议的重复问题相反)。所以这不是系统定时器分辨率的问题。所以,所谓的重复题,并没有什么有用的信息。背景:似乎.NETSystem.Threading.Timer没有使用系统时钟分辨率-它保持~15ms分辨率。尽管操作系统时钟(例如睡眠分辨率)更精确。在我的盒子上(当几乎空闲且有4个内核可用时):>Clockres.exeClockResv2.0-查看系统时钟分辨率版权所有(C)2009MarkRussinovichSysInternals-www.sysinternals.com最大定时器间隔:15.625毫秒最小定时器间隔:0.500毫秒当前计时器间隔:1.001毫秒输出我的快速测试:睡眠测试:平均时间增量:2[ms](来自993个案例)System.Threading.Timer测试:平均时间增量:15[ms](来自985个案例))测试代码是:privatestaticvoidTestSleepVsTimer(longmillisecondsDifference,intrepetions){TimingEventsKeepertimingEventsKeeper=newTimingEventsKeeper();timingEventsKeeper.Reset((int)毫秒差异,重复);while(!timingEventsKeeper.TestDoneEvent.IsSet){timingEventsKeeper.CountNextEvent(null);Thread.Sleep((int)毫秒差异);}Console.WriteLine("睡眠测试:");timingEventsKeeper.Output();timingEventsKeeper.Reset((int)millisecondsDifference,repetitions);t=newTimer(timingEventsKeeper.CountNextEvent,null,TimeSpan.FromMilliseconds(1),TimeSpan.FromMilliseconds(1));timingEventsKeeper.TestDoneEvent.Wait();Console.WriteLine("System.Threading.Timer测试:");timingEventsKeeper.Output();}私有类TimingEventsKeeper{long_ticksSum=0;长_casesCount=0;长_minTicksDiff;长_maxTicksDiff;长_lastTicksCount;int_repetitons;publicCountdownEventTestDoneEvent=newCountdownEvent(0);publicvoidReset(intmillisecondsDifference,intrepetitions){_ticksSum=0;_casesCount=0;_minTicksDiff=毫秒差*10000;_maxTicksDiff=毫秒差*10000;_lastTicksCount=DateTime.UtcNow.Ticks;_repetitons=重复;TestDoneEvent.Reset(重复);}publicvoidCountNextEvent(objectunused){longcurrTicksCount=DateTime.UtcNow.Ticks;longdiff=currTicksCount-_lastTicksCount;_lastTicksCount=currTicksCount;TestDoneEvent.Signal();如果(差异>=_maxTicksDiff){_maxTicksDiff=差异;返回;}如果(diff0)Console.WriteLine("平均时间增量:{0}[ms](来自{1}个案例)",_ticksSum/_casesCount/10000,_casesCount);elseConsole.WriteLine("没有要计算平均值的测量案例");}}publicstaticclassWinApi{///TimeBeginPeriod()。有关详细信息,请参阅WindowsAPI文档。[System.Diagnostics.CodeAnalysis.SuppressMessage(“Microsoft.Interoperability”,“CA1401:PInvokesShouldNotBeVisible”),System.Diagnostics.CodeAnalysis.SuppressMessage(“Microsoft.Security”,“CA2118:ReviewSuppressUnmanagedCodeSecurityUsage”),SuppressUnmanagedCodeSecurity][DllImport(“winmm.dll",EntryPoint="timeBeginPeriod",SetLastError=true)]publicstaticexternuintTimeBeginPeriod(uintuMilliseconds);///时间结束时间()。有关详细信息,请参阅WindowsAPI文档。[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability","CA1401:PInvokesShouldNotBeVisible"),System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security","CA2118:ReviewSuppressUnmanagedCodeSecurityUsage"),SuppressUnmanagedCodeSecurity][DllImport("winmm.dll",EntryPoint="timeEndPeriod",SetLastError=true)]publicstaticexternuintTimeEndPeriod(uintuMilliseconds);}私有静态(stringvoidMainar){WinApi.TimeBeginPeriod(1);TestSleepVsTimer(1,1000);WinApi.TimeEndPeriod(1);}EDIT1:环境:在.NET2.0、3.0、3.5(无CountDownEvent)和Windows8(Build9200)、Server2012(Build9200)上的4.5),在Server2008(Build6001SP1)下的构建和发布版本上测试,Sleep和Timer之间存在显着差异为什么这不是重复的:正如我发布的那样-OSTimerResolutionSettingsis1ms(并且Sleep不显示这种行为)。所以这不是操作系统计时器分辨率(中断频率)的错误-这是System.Threading.Timer特有的东西。EDIT2?添加了TimeBeginPeriod和TimeEndPeriod调用代码-强制操作系统计时器分辨率更改)为什么System.Threading.Timer保持15ms分辨率,尽管OS时钟分辨率更精确?显然是由于实施。System.Threading.Timer(和Task.Delay)使用.NET运行时来计时计时器队列,而不管系统计时器分辨率如何。此外,我在Windows(7,10;服务器2012、2016)上运行测试(.net4.x),发现WaitHandle.WaitOne()和Monitor.Wait()也不尊重系统计时器分辨率(这是上面的答案使用WaitHandle)。那么,只有Thread.Sleep尊重它吗?(是的,我没有测试信号量和互斥量)。在没有繁忙的CPU等待的情况下实现1ms定时事件解析的可行方法是什么?JimMischel指出的一种方法。但是,它也有一些缺点,例如:回调是在Windows线程池线程上执行的。时间间隔是相对于当前时间的。时间间隔是毫秒的整数,所以理论上最大精度是1毫秒。根据许多报告,仅使用timeBeginPeriod(1)调用,1.5-2毫秒的精度几乎是您可以达到的最大值。另一种方法是:NtSetTimerResolution和WaitableTimerObjects。您可以获得0.5毫秒的分辨率(取决于硬件和Windows版本)。对于c#示例(它不是您的计时器类的示例,而是在c#中使用此函数的示例),您可以查看这篇文章。使用从WaitHandle派生的同步类之一,例如AutoResetEvent或ManualResetEvent,在调用WaitOne()方法时设置超时参数。通过在循环中调用WaitOne,您可以实现一个计时器。您可以向等待句柄派生类发出信号以断开或中断计时器。注意,要更改比例,最好使用现IDisposable的帮助程序类:internalsealedclassTimePeriod:IDisposable{privateconststringWINMM="winmm.dll";私有静态TIMECAPStimeCapabilities;私有静态intinTimePeriod;私人只读期间;私有int处置;[DllImport(WINMM,ExactSpelling=true)]privatestaticexterninttimeGetDevCaps(refTIMECAPSptc,intcbtc);[DllImport(WINMM,ExactSpelling=true)]privatestaticexterninttimeBeginPeriod(intuPeriod);[DllImport(WINMM,ExactSpelling=true)]privatestaticexterninttimeEndPeriod(intuPeriod);staticTimePeriod(){intresult=timeGetDevCaps(reftimeCapabilities,Marshal.SizeOf(typeof(TIMECAPS)));if(result!=0){thrownewInvalidOperationException("获取时间功能的请求未完成,因为代码为"+result+"发生意外错误。");}}internalTimePeriod(intperiod){if(Interlocked.Increment(refinTimePeriod)!=1){Interlocked.Decrement(refinT时间段);thrownewNotSupportedException("进程已经在一个时间段内,不支持嵌套时间段。");}if(periodtimeCapabilities.wPeriodMax){thrownewArgumentOutOfRangeException("period","开始时间段的请求未完成,因为指定的分辨率超出范围。");}intresult=timeBeginPeriod(period);if(result!=0){thrownewInvalidOperationException("开始时间段的请求未完成,因为代码为"+result+"的意外错误发生了。");}this.period=period;}internalstaticintMinimumPeriod{get{returntimeCapabilities.wPeriodMin;}}internalstaticintMaximumPeriod{得到{返回timeCapabilities.wPeriodMax;}}internalintPeriod{get{if(this.disposed>0){thrownewObjectDisposedException("时间段实例已被释放。");}返回this.period;}}publicvoidDispose(){if(Interlocked.Increment(refthis.disposed)==1){timeEndPeriod(this.period);Interlocked.Decrement(refinTimePeriod);}else{Interlocked.Decrement(refthis.disposed);}}[StructLayout(LayoutKind.Sequential)]privatestructTIMECAPS{内部intwPeriodMin;内部intwPeriodMax;}}然后就可以使用:using(newTimePeriod(1)){////...}以上是C#学习教程:System.Threading.TimervsSystem.Threading.Thread。睡眠分辨率——.NETTimer不使用系统时钟分辨率来共享所有内容。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权请点击右侧联系管理员删除。如需转载请注明出处: