3

有没有一种方法可以在不使用线程池的情况下在 C# 中执行周期性任务/线程(这是我的主要要求之一)。

预期的场景如下:

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

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

System.Timers.Timer似乎能够通过该属性处理此问题SynchronizingObject。但是,我不确定如何ISynchronizeInvoke根据上述情况实现 a 。我发现的所有用于实现的代码示例ISynchronizeInvoke对于处理这样一个简单的场景来说似乎都太复杂了。

有没有一种方法可以在 C# 中轻松实现此功能,如上述场景(不使用线程池)并且比System.Forms.Timer

4

4 回答 4

4

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

Windows 中默认的系统范围定时器分辨率为 15.6 毫秒,这意味着操作系统每 15.6 毫秒接收一次来自系统定时器硬件的时钟中断。当时钟中断触发时,Windows 会更新计时器滴答计数并检查计划的计时器对象是否已过期。一些应用程序要求比每 15.6 毫秒更频繁地服务计时器到期。

他们还说:

应用程序可以调用 timeBeginPeriod 来增加计时器的分辨率。最大分辨率为 1 毫秒,用于支持图形动画、音频播放或视频播放。这不仅将应用程序的计时器分辨率提高到 1 毫秒,而且还会影响全局系统计时器分辨率,因为 Windows 至少使用任何应用程序请求的最高分辨率(即最低间隔)。因此,如果只有一个应用程序请求 1 ms 的计时器分辨率,系统计时器会将间隔(也称为“系统计时器滴答”)设置为至少 1 ms。有关详细信息,请参阅 MSDN® 网站上的“timeBeginPeriod 函数”。

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

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

于 2013-10-13T03:31:32.163 回答
2

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

如果您发现不需要 Multimedia Timers 提供的分辨率,您会发现Waitable Timers让您能够编写一个循环,在单个线程中等待计时器到期。

您也可以选择Timer Queues,它提供大量计时器,而没有其他计时器的资源开销,但默认使用系统线程池。

于 2013-10-13T05:31:04.280 回答
0

您是否尝试过简单地休眠线程?

int msecSleepTime = 25;  // number of milliseconds to sleep
Thread.Sleep(msecSleepTime);  // sleep the current thread for the given number of milliseconds
于 2013-10-13T03:14:19.923 回答
0

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());
    }
}
于 2013-10-13T06:04:25.397 回答