您的位置:首页 > 游戏 > 游戏 > 网站建设流程包括哪些内容_网站开发简历_海外seo_seo扣费系统

网站建设流程包括哪些内容_网站开发简历_海外seo_seo扣费系统

2024/12/23 15:47:54 来源:https://blog.csdn.net/weixin_38428126/article/details/144287766  浏览:    关键词:网站建设流程包括哪些内容_网站开发简历_海外seo_seo扣费系统
网站建设流程包括哪些内容_网站开发简历_海外seo_seo扣费系统

在C#日常开发中,我们经常需要使用定时器(Timer)进行周期性任务的执行。
例如,每隔1秒打印一条日志,或每隔100毫秒执行某个数据刷新逻辑。

但是,当我们尝试在C#中实现一个1毫秒(1ms)级别的高精度周期性定时时,往往会发现结果并不如预期理想。

标准的托管定时器(如 System.Threading.TimerSystem.Timers.Timer)虽然能简单快速地实现周期性调用,但在1ms级别的精度要求下却经常出现几ms延迟甚至几十ms的偏差。

测试下来是和电脑的性能会有关系。

本文将深入分析为什么在C#中难以实现精确的1ms定时,并给出解决方案。

我们将利用 Windows 的多媒体定时器(Multimedia Timer)来显著提高定时精度,并给出完整的示例代码,让您在实际项目中轻松上手。

为什么C#的托管定时器不精确?

1. 操作系统定时器分辨率的限制

Windows系统本身对计时有一定的粒度限制。默认情况下,Windows的系统计时器频率可能在10~15.6ms左右。当您使用标准托管定时器设定间隔为1ms时,其实是向操作系统提出了一个“请求”,但由于底层定时器分辨率所限,最终触发往往会推迟到下一个系统计时点,导致数毫秒到十几毫秒的延迟。这也意味着在1秒内,您可能只会收到数百次回调,而非理想中的1000次。

2. 托管环境和垃圾回收(GC)

C# 运行在 .NET CLR(或 .NET Runtime)之上,这是一层托管运行环境。托管运行时需要进行垃圾回收、线程调度和JIT编译等工作。这些过程会引入额外的延迟和不确定性,使定时器触发时间更加不可控。在满负载或GC频繁触发时,定时回调的执行可能被进一步推迟。

3. 线程池和任务调度

System.Threading.TimerSystem.Timers.Timer 通常基于线程池调度任务。线程池不是为毫秒级别的实时触发而设计的,它更倾向于在较低精度要求下的周期性或延迟任务。因此,即使设定了1ms的间隔,实际触发时刻仍会受到线程池任务排队和分配的影响。

如何提升定时精度?

若要实现接近1ms级别的定时精度,需要采取以下策略:

  1. 提高系统定时器精度
    使用 timeBeginPeriod(1) 来设置系统全局计时器分辨率为1ms。此举会让Windows的定时粒度更精细,让后续的定时调用有更高概率以1ms级别进行触发。需要注意,提高系统计时精度会增加系统整体负载与耗电。

  2. 使用多媒体定时器(timeSetEvent)
    多媒体定时器是Windows早期为音视频播放等对时序要求较高的场景而设计的API。与标准托管定时器相比,多媒体定时器能在极短的间隔内更加稳定地触发回调,从而提升定时精度。

  3. 使用原子操作和低延迟回调
    在回调中使用 Interlocked 系列函数来确保多线程场景下的计数操作是线程安全和低开销的。同时避免在回调中执行过于复杂的逻辑,将回调尽可能简化以缩短执行时间,降低后续触发的抖动。

  4. 考虑实时系统或硬件计时(如果要求更高):
    当需要真正严格的实时性能(如工业控制或设备驱动开发),Windows并非最佳平台。这时应考虑实时操作系统(RTOS)、专用硬件计数器或微控制器,以达到真正的毫秒乃至微秒级精度。

使用多媒体定时器的完整示例代码

下面的代码展示了如何使用多媒体定时器 timeSetEvent 来实现一个1ms定时器和一个1s定时器:

  • 1ms定时器:每1ms增加一次计数器 _counter
  • 1s定时器:每1秒读取 _counter 值并打印,然后重置为0
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;class Program
{[DllImport("winmm.dll", SetLastError = true)]private static extern uint timeSetEvent(uint uDelay,uint uResolution,TimeProc lpTimeProc,IntPtr dwUser,uint fuEvent);[DllImport("winmm.dll", SetLastError = true)]private static extern uint timeKillEvent(uint uTimerId);[DllImport("winmm.dll", SetLastError = true)]private static extern int timeBeginPeriod(uint uMilliseconds);[DllImport("winmm.dll", SetLastError = true)]private static extern int timeEndPeriod(uint uMilliseconds);private delegate void TimeProc(uint uTimerID, uint uMsg, IntPtr dwUser, IntPtr dw1, IntPtr dw2);private static uint _timerId1ms;private static uint _timerId1s;private static long _counter = 0;private static TimeProc _timeProc1ms;private static TimeProc _timeProc1s;static void Main(){// 提高系统定时器精度到1mstimeBeginPeriod(1);_timeProc1ms = new TimeProc((uTimerID, uMsg, dwUser, dw1, dw2) =>{// 每1ms增加一次计数Interlocked.Increment(ref _counter);});_timeProc1s = new TimeProc((uTimerID, uMsg, dwUser, dw1, dw2) =>{// 每1s读取并打印计数long currentCount = Interlocked.Read(ref _counter);Debug.WriteLine($"当前计数值: {currentCount}");Interlocked.Exchange(ref _counter, 0);});// 启动1ms定时器(周期性)_timerId1ms = timeSetEvent(1, 0, _timeProc1ms, IntPtr.Zero, 1);if (_timerId1ms == 0){Console.WriteLine("1ms定时器创建失败");Cleanup();return;}// 启动1s定时器(周期性)_timerId1s = timeSetEvent(1000, 0, _timeProc1s, IntPtr.Zero, 1);if (_timerId1s == 0){Console.WriteLine("1s定时器创建失败");Cleanup();return;}Console.WriteLine("按下任意键退出...");Console.ReadKey();Cleanup();}private static void Cleanup(){if (_timerId1ms != 0){timeKillEvent(_timerId1ms);_timerId1ms = 0;}if (_timerId1s != 0){timeKillEvent(_timerId1s);_timerId1s = 0;}timeEndPeriod(1);}
}

运行后效果
您将看到在控制台或Debug输出中,每秒打印一次计数值。与托管定时器相比,使用多媒体定时器后,每秒计数的值通常能更接近1000。尽管仍可能受到系统负载影响,但相较常规定时器,这已经是一个显著的提升。

总结

  • 在C#中尝试实现1ms精度的定时器时,使用默认托管定时器往往不够精确。
  • 系统定时器分辨率、托管运行时环境和线程调度机制都会影响定时精度。
  • 通过使用 timeBeginPeriod 和多媒体定时器 timeSetEvent,我们可以大幅提升定时器的精度和稳定性。
  • 若有更高的实时性需求,可考虑使用实时操作系统或硬件定时器来获得更高精度。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com