不使用线程池的 C# 中的定期任务

本文关键字:任务 线程 | 更新日期: 2023-09-27 18:35:03

有没有办法在不使用线程池的情况下在 C# 中执行定期任务/线程(这是我的主要要求之一(。

预期方案如下:

  1. 定期线程阻塞/等待计时器事件(来自系统(。
  2. 当计时器过期时,系统会通知线程。
  3. 线程执行一些代码。
  4. 线程
  5. 阻止/等待来自系统的下一个计时器事件(请注意,如果线程错过了一个或多个计时器事件,这是可以的(。

我发现最接近这一点的是 System.Forms.Timer ,但问题是它的精度仅限于 55 毫秒,这对我来说还不够。

System.Timers.Timer似乎能够通过SynchronizingObject属性处理此问题。但是,我不太确定如何根据上述情况实现ISynchronizeInvoke。我找到的所有用于实现ISynchronizeInvoke的代码示例似乎都太复杂了,无法处理如此简单的场景。

有没有办法像上述场景一样在 C# 中轻松实现此功能(不使用线程池(并且比System.Forms.Timer更准确?

不使用线程池的 C# 中的定期任务

Windows 计时器的默认分辨率为 15.6 毫秒,来自其文档:

Windows 中默认的系统范围计时器分辨率为 15.6 毫秒,这 意味着操作系统每 15.6 毫秒接收一个时钟 从系统计时器硬件中断。当时钟中断时 触发时,Windows 更新计时器时钟周期计数并检查是否 计划计时器对象已过期。某些应用程序要求 计时器过期的服务频率高于每 15.6 毫秒一次。

他们还指出:

应用程序可以调用 timeBeginPeriod 来增加计时器 分辨率。最大分辨率为 1 毫秒用于支持 图形动画、音频播放或视频播放。这不仅如此 将应用程序的计时器分辨率提高到 1 毫秒,但也 影响全局系统计时器分辨率,因为 Windows 使用 最低分辨率(即最低间隔( 应用程序请求。因此,如果只有一个应用程序请求 定时器分辨率为 1 ms,系统定时器设置间隔(也 称为"系统计时器滴答"(至少 1 毫秒。欲了解更多信息 信息,请参阅 MSDN® 网站上的"时间开始周期函数"。

因此,如果您确实需要高分辨率计时器,则可以使用 timeBeginPeriod 调用来影响全局系统计时器,但要知道它会影响其工作设备的电池寿命/功耗。

此外,还有 QueryPerformanceCounter 和 QueryPerformanceFrequency 调用,可用于在 .net 中获得较低级别的精度,如以下示例所示:http://www.codeproject.com/Articles/571289/Obtaining-Microsecond-Precision-in-NET

多媒体定时器将为您提供所有定时器中最高分辨率。如果您真的需要10ms或更低的分辨率,那将是唯一的方法。

如果您发现不需要多媒体计时器提供的分辨率,您会发现可等待计时器将使您能够编写一个循环,该循环等待计时器在单个线程中过期。

您还可以选择计时器队列,它提供大量计时器,而没有其他计时器的资源开销,但默认情况下使用系统线程池。

您是否尝试过简单地睡掉您的线程?

int msecSleepTime = 25;  // number of milliseconds to sleep
Thread.Sleep(msecSleepTime);  // sleep the current thread for the given number of milliseconds

无需重新配置系统计时器的情况下,系统上System.Timers.Timer的最快响应为 ~16 毫秒。 将Timer.Interval值设置为 (int)( target interval / 16 ) * 16然后让线程在剩余的持续时间内休眠(如果您确实需要更好的准确性,解释型语言(如 C#(不是正确的选择(:

class Program
{
    private static Timer _timer = new Timer();
    private static DateTime _last = DateTime.Now;
    private const int IntendedInterval = 25;
    static void Main(string[] args)
    {
        _timer.AutoReset = false;
        _timer.Interval = (int)(IntendedInterval / 16) * 16;
        _timer.Elapsed += _timer_Elapsed;
        _timer.Start();
        Console.ReadLine();
    }
    static void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        var now = DateTime.Now;
        var duration = now - _last;
        var sleep = IntendedInterval - duration.TotalMilliseconds;
        if (0 < sleep )
        {
            System.Threading.Thread.Sleep(( int )sleep);
        }
        _timer.Start();
        now = DateTime.Now;
        duration = now - _last;
        _last = now;
        Console.WriteLine(duration.TotalMilliseconds.ToString());
    }
}