高分辨率计时器

sur*_*ura 15 c#

我想要一个大约5毫秒分辨率的计时器.但.Net中的当前Timer具有大约50ms的分辨率.我找不到任何可以创建高分辨率计时器的工作解决方案,尽管有些人声称你可以用C#来做.

Mik*_*ley 15

关于OP特别询问有关Timer定期发生事件的班级的信息.我修改了这个答案,我的回答低于横向规则.

我使用Timer类测试了以下代码,看起来它至少可以在我的机器上的14-15毫秒范围内.亲自尝试一下,看看你是否可以重现这一点.因此,可能有不到50毫秒的响应时间,但它不能精确到一毫秒.

using System;
using System.Timers;
using System.Diagnostics;

public static class Test
{
    public static void Main(String[] args)
    {
        Timer timer = new Timer();
        timer.Interval = 1;
        timer.Enabled = true;

        Stopwatch sw = Stopwatch.StartNew();
        long start = 0;
        long end = sw.ElapsedMilliseconds;

        timer.Elapsed += (o, e) =>
        {
            start = end;
            end = sw.ElapsedMilliseconds;
            Console.WriteLine("{0} milliseconds passed", end - start);
        };

        Console.ReadLine();
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:以下是我的老答案,当时我认为OP正在谈论计时.以下仅仅是关于事物持续时间的定时的有用信息,但是没有提供以规则间隔触发事件的任何方式.为此目的,该Timer课程是必要的.

尝试使用秒表类System.Diagnostics:http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx

您可以查询它以通过它的IsHighResolution字段检查它是否是高分辨率.此外,您可以检查秒表的确切分辨率:

int resolution = 1E9 / Stopwatch.Frequency;
Console.WriteLine("The minimum measurable time on this system is: {0} nanoseconds", resolution);
Run Code Online (Sandbox Code Playgroud)

如果您担心实际来源的位置,那么文档似乎暗示它实际上在内部调用较低级别的Win32函数:

秒表类可以帮助管理托管代码中与时序相关的性能计数器.具体来说,可以使用Frequency字段和GetTimestamp方法代替非托管Win32 API QueryPerformanceFrequency和QueryPerformanceCounter.


gen*_*sis 11

一个怎么样?

public class HiResTimer
{
    private bool isPerfCounterSupported = false;
    private Int64 frequency = 0;

    // Windows CE native library with QueryPerformanceCounter().
    private const string lib = "coredll.dll";
    [DllImport(lib)]
    private static extern int QueryPerformanceCounter(ref Int64 count);
    [DllImport(lib)]
    private static extern int QueryPerformanceFrequency(ref Int64 frequency);

    public HiResTimer()
    {
        // Query the high-resolution timer only if it is supported.
        // A returned frequency of 1000 typically indicates that it is not
        // supported and is emulated by the OS using the same value that is
        // returned by Environment.TickCount.
        // A return value of 0 indicates that the performance counter is
        // not supported.
        int returnVal = QueryPerformanceFrequency(ref frequency);

        if (returnVal != 0 && frequency != 1000)
        {
            // The performance counter is supported.
            isPerfCounterSupported = true;
        }
        else
        {
            // The performance counter is not supported. Use
            // Environment.TickCount instead.
            frequency = 1000;
        }
    }

    public Int64 Frequency
    {
        get
        {
            return frequency;
        }
    }

    public Int64 Value
    {
        get
        {
            Int64 tickCount = 0;

            if (isPerfCounterSupported)
            {
                // Get the value here if the counter is supported.
                QueryPerformanceCounter(ref tickCount);
                return tickCount;
            }
            else
            {
                // Otherwise, use Environment.TickCount.
                return (Int64)Environment.TickCount;
            }
        }
    }

    static void Main()
    {
        HiResTimer timer = new HiResTimer();

        // This example shows how to use the high-resolution counter to 
        // time an operation. 

        // Get counter value before the operation starts.
        Int64 counterAtStart = timer.Value;

        // Perform an operation that takes a measureable amount of time.
        for (int count = 0; count < 10000; count++)
        {
            count++;
            count--;
        }

        // Get counter value when the operation ends.
        Int64 counterAtEnd = timer.Value;

        // Get time elapsed in tenths of a millisecond.
        Int64 timeElapsedInTicks = counterAtEnd - counterAtStart;
        Int64 timeElapseInTenthsOfMilliseconds =
            (timeElapsedInTicks * 10000) / timer.Frequency;

        MessageBox.Show("Time Spent in operation (tenths of ms) "
                       + timeElapseInTenthsOfMilliseconds +
                       "\nCounter Value At Start: " + counterAtStart +
                       "\nCounter Value At End : " + counterAtEnd +
                       "\nCounter Frequency : " + timer.Frequency);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • +1表示手头上有这么大的课程.当`System.Diagnostics`中的`Stopwatch`类完成相同的操作时,会出现不必要的复杂情况.我不确定他们是否在VS 2005中暴露了这个,但它现在存在并且没有理由使用这样的东西. (5认同)
  • 我不知道秒表的精确度如何,但性能计数器会让你进入低微秒范围.我也想知道是什么让它成为一个"大"或"复杂"的类.我应该说"-1"吗? (5认同)
  • 引用文档:`Stopwatch类可以帮助管理托管代码中与时序相关的性能计数器.具体来说,可以使用Frequency字段和GetTimestamp方法代替非托管的Win32 API QueryPerformanceFrequency和QueryPerformanceCounter.因此不需要使用这种代码.此外,在秒表使用起来更简单的意义上,这是"大"的. (5认同)
  • @genesis:这是一个有效的解决方案,但我建议远离它,因为"秒表"是一种更惯用(更不用说更直接)的方式来完成同样的事情.我不会给这个-1,但我会提醒人们不要这样做,而是转向秒表. (3认同)
  • 他想要工作解决方案,所以我给了他一个.我认为没有理由进行downvote,因为这是有效的 (2认同)

sur*_*ura 7

我在以下博客中找到了解决此问题的方法:http://web.archive.org/web/20110910100053/http: //www.indigo79.net/archives/27#comment-255

它告诉您如何使用多媒体计时器来设置高频定时器.它对我来说工作得很好!!!

  • 是的,确实链接坏了.看到这就是为什么我畏缩链接到外部网站的帖子作为答案,并没有意识到外部网站或网页可能随时被破坏,就像在这种情况下.毋庸置疑,Sura的答案现在对StackOverflow社区毫无用处,即使它对他或她有用. (2认同)

Maj*_*cRa 5

这是基于秒表计时器的实现
https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545

  1. 它适用于所有平台,并且在任何地方都具有高精度 StopWatch.IsHighPrecision == true

  2. 它的Elapsed事件保证不重叠(这可能很重要,因为事件处理程序内的状态更改可能不受多线程访问的保护)

以下是如何使用它:

Console.WriteLine($"IsHighResolution = {HighResolutionTimer.IsHighResolution}");
Console.WriteLine($"Tick time length = {HighResolutionTimer.TickLength} [ms]");

var timer = new HighResolutionTimer(0.5f);

// UseHighPriorityThread = true, sets the execution thread 
// to ThreadPriority.Highest.  It doesn't provide any precision gain
// in most of the cases and may do things worse for other threads. 
// It is suggested to do some studies before leaving it true
timer.UseHighPriorityThread = false;

timer.Elapsed += (s, e) => { /*... e.Delay*/ }; // The call back with real delay info
timer.Start();  
timer.Stop();    // by default Stop waits for thread.Join()
                 // which, if called not from Elapsed subscribers,
                 // would mean that all Elapsed subscribers
                 // are finished when the Stop function exits 
timer.Stop(joinThread:false)   // Use if you don't care and don't want to wait
Run Code Online (Sandbox Code Playgroud)

这是一个基准(和一个活生生的例子):https :
//gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3

在 Windows 10 上将计时器设置为 0.5 毫秒的结果: 在此处输入图片说明

还值得一提的是:

  1. 我在 Ubuntu 上对单声道有同样的精度。

  2. 在玩基准测试时,我看到的最大和非常罕见的偏差约为 0.5 毫秒(这可能没有任何意义,它不是实时系统,但仍然值得一提)

  3. 秒表刻度不是 TimeSpan 刻度。在该 Windows 10 机器上 HighResolutionTimer.TickLength 为 0.23[ns]。

  4. 基准测试的CPU 使用率为 0.5ms 间隔为 10%,200ms 间隔为 0.1%