C# C语言中的高分辨率定时器#

C# C语言中的高分辨率定时器#,c#,timer,high-resolution,C#,Timer,High Resolution,是否有一个高分辨率计时器,它会在每次计时器过期时引发一个事件,就像System.timer类一样?我需要一个高分辨率计时器,以每毫秒运行一次 我经常看到一些帖子解释秒表可以测量高分辨率,但我不想测量时间,我想创建一个1毫秒的间隔 NET中有什么东西吗?还是我要编写自己的高分辨率计时器?尝试创建新的System.Threading.Thread并使用System.Threading.Thread.Sleep var thrd = new Syatem.Threading.Thread(() =&g

是否有一个高分辨率计时器,它会在每次计时器过期时引发一个事件,就像
System.timer
类一样?我需要一个高分辨率计时器,以
每毫秒运行一次

我经常看到一些帖子解释秒表可以测量高分辨率,但我不想测量时间,我想创建一个1毫秒的间隔


NET中有什么东西吗?还是我要编写自己的高分辨率计时器?

尝试创建新的
System.Threading.Thread
并使用
System.Threading.Thread.Sleep

var thrd = new Syatem.Threading.Thread(() => {
    while (true) {
        // do something
        System.Threading.Thread.Sleep(1); // wait 1 ms
    }
});

thrd.Start();

据我所知,.NET框架中没有内置任何内容。Windows具有通过的高分辨率计时器事件机制。下面是一个快速的例子,我突然想到这似乎做的工作。似乎还有一个很好的例子

我会注意到,这个API改变了系统范围的设置,这会降低系统性能,所以买家要小心。出于测试目的,我建议跟踪定时器触发的频率,以验证计时是否与您尝试模拟的设备相似。由于windows不是实时操作系统,系统上的负载可能会导致MM计时器延迟,导致100毫秒的间隔包含100个快速连续的事件,而不是间隔1毫秒的100个事件。有些是MM定时器

class Program
{
    static void Main(string[] args)
    {
        TestThreadingTimer();
        TestMultimediaTimer();
    }

    private static void TestMultimediaTimer()
    {
        Stopwatch s = new Stopwatch();
        using (var timer = new MultimediaTimer() { Interval = 1 })
        {
            timer.Elapsed += (o, e) => Console.WriteLine(s.ElapsedMilliseconds);
            s.Start();
            timer.Start();
            Console.ReadKey();
            timer.Stop();
        }
    }

    private static void TestThreadingTimer()
    {
        Stopwatch s = new Stopwatch();
        using (var timer = new Timer(o => Console.WriteLine(s.ElapsedMilliseconds), null, 0, 1))
        {
            s.Start();
            Console.ReadKey();
        }
    }

}

public class MultimediaTimer : IDisposable
{
    private bool disposed = false;
    private int interval, resolution;
    private UInt32 timerId; 

    // Hold the timer callback to prevent garbage collection.
    private readonly MultimediaTimerCallback Callback;

    public MultimediaTimer()
    {
        Callback = new MultimediaTimerCallback(TimerCallbackMethod);
        Resolution = 5;
        Interval = 10;
    }

    ~MultimediaTimer()
    {
        Dispose(false);
    }

    public int Interval
    {
        get
        {
            return interval;
        }
        set
        {
            CheckDisposed();

            if (value < 0)
                throw new ArgumentOutOfRangeException("value");

            interval = value;
            if (Resolution > Interval)
                Resolution = value;
        }
    }

    // Note minimum resolution is 0, meaning highest possible resolution.
    public int Resolution
    {
        get
        {
            return resolution;
        }
        set
        {
            CheckDisposed();

            if (value < 0)
                throw new ArgumentOutOfRangeException("value");

            resolution = value;
        }
    }

    public bool IsRunning
    {
        get { return timerId != 0; }
    }

    public void Start()
    {
        CheckDisposed();

        if (IsRunning)
            throw new InvalidOperationException("Timer is already running");

        // Event type = 0, one off event
        // Event type = 1, periodic event
        UInt32 userCtx = 0;
        timerId = NativeMethods.TimeSetEvent((uint)Interval, (uint)Resolution, Callback, ref userCtx, 1);
        if (timerId == 0)
        {
            int error = Marshal.GetLastWin32Error();
            throw new Win32Exception(error);
        }
    }

    public void Stop()
    {
        CheckDisposed();

        if (!IsRunning)
            throw new InvalidOperationException("Timer has not been started");

        StopInternal();
    }

    private void StopInternal()
    {
        NativeMethods.TimeKillEvent(timerId);
        timerId = 0;
    }

    public event EventHandler Elapsed;

    public void Dispose()
    {
        Dispose(true);
    }

    private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2)
    {
        var handler = Elapsed;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }

    private void CheckDisposed()
    {
        if (disposed)
            throw new ObjectDisposedException("MultimediaTimer");
    }

    private void Dispose(bool disposing)
    {
        if (disposed)
            return;
        
        disposed = true;
        if (IsRunning)
        {
            StopInternal();
        }
        
        if (disposing)
        {
            Elapsed = null;
            GC.SuppressFinalize(this);
        }
    }
}

internal delegate void MultimediaTimerCallback(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);

internal static class NativeMethods
{
    [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
    internal static extern UInt32 TimeSetEvent(UInt32 msDelay, UInt32 msResolution, MultimediaTimerCallback callback, ref UInt32 userCtx, UInt32 eventType);

    [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")]
    internal static extern void TimeKillEvent(UInt32 uTimerId);
}
类程序
{
静态void Main(字符串[]参数)
{
TestThreadingTimer();
TestMultimediaTimer();
}
私有静态void TestMultimediaTimer()
{
秒表s=新秒表();
使用(var timer=new MultimediaTimer(){Interval=1})
{
timer.appeated+=(o,e)=>Console.WriteLine(s.elapsedmillyses);
s、 Start();
timer.Start();
Console.ReadKey();
timer.Stop();
}
}
私有静态void TestThreadingTimer()
{
秒表s=新秒表();
使用(var timer=new timer(o=>Console.WriteLine(s.elapsedmillisons),null,0,1))
{
s、 Start();
Console.ReadKey();
}
}
}
公共类多媒体计时器:IDisposable
{
私有布尔=假;
私有整数间隔,分辨率;
私人UInt32 timerId;
//按住计时器回调以防止垃圾回收。
专用只读多媒体TimerCallback回调;
公共多媒体计时器()
{
回调=新的多媒体TimerCallback(TimerCallback方法);
分辨率=5;
间隔=10;
}
~MultimediaTimer()
{
处置(虚假);
}
公共整数间隔
{
得到
{
返回间隔;
}
设置
{
CheckDisposed();
如果(值<0)
抛出新ArgumentOutOfRangeException(“值”);
间隔=值;
如果(分辨率>间隔)
分辨率=数值;
}
}
//注:最小分辨率为0,表示可能的最高分辨率。
公共整数分辨率
{
得到
{
返回分辨率;
}
设置
{
CheckDisposed();
如果(值<0)
抛出新ArgumentOutOfRangeException(“值”);
分辨率=数值;
}
}
公共图书馆正在运行
{
获取{return timerId!=0;}
}
公开作废开始()
{
CheckDisposed();
如果(正在运行)
抛出新的InvalidOperationException(“计时器已在运行”);
//事件类型=0,一次性事件
//事件类型=1,周期性事件
UInt32 userCtx=0;
timerId=NativeMethods.TimeSetEvent((uint)Interval,(uint)Resolution,Callback,ref userCtx,1);
if(timerId==0)
{
int error=Marshal.GetLastWin32Error();
抛出新的Win32Exception(错误);
}
}
公共停车场()
{
CheckDisposed();
如果(!正在运行)
抛出新的InvalidOperationException(“计时器未启动”);
StopInternal();
}
私有void StopInternal()
{
NativeMethods.TimeKillEvent(timerId);
timerId=0;
}
公共事件事件处理程序;
公共空间处置()
{
处置(真实);
}
私有无效TimerCallbackMethod(uint id、uint msg、ref uint userCtx、uint rsv1、uint rsv2)
{
var handler=已用时间;
if(处理程序!=null)
{
处理程序(此,EventArgs.Empty);
}
}
私有void CheckDisposed()
{
如果(已处置)
抛出新的ObjectDisposedException(“多媒体计时器”);
}
私有无效处置(bool处置)
{
如果(已处置)
返回;
这是真的;
如果(正在运行)
{
StopInternal();
}
如果(处置)
{
经过=空;
总干事(本);
}
}
}
内部委托无效多媒体时间回调(UInt32 id、UInt32 msg、ref UInt32 userCtx、UInt32 rsv1、UInt32 rsv2);
内部静态类NativeMethods
{
[DllImport(“winmm.dll”,SetLastError=true,EntryPoint=“timeSetEvent”)]
内部静态外部UInt32时间事件(UInt32 msDelay、UInt32 msResolution、多媒体时间回调、ref UInt32 userCtx、UInt32事件类型);
[DllImport(“winmm.dll”,SetLastError=true,EntryPoint=“timeKillEvent”)]
内部静态外部无效TimeKillEvent(UInt32 uTimerId);
}

有一个选项:使用
线程。睡眠(0)
。尝试调用
Thread.Sleep(1)
或使用
系统。Threading.Timer
始终取决于系统计时器的分辨率。根据具体情况,这可能不是最好的主意,在一天结束时,可能不允许您的应用程序从winmm.dll调用
timeBeginPeriod(…)

在我的开发机器(i7q)上,以下代码可以解析为+/-10ns(0.10ms),可能是hi
var requiredDelayMs = 0.1;
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
while (true)
{
    if (sw.Elapsed.TotalMilliseconds >= requiredDelayMs) 
    {
      // call your timer routine
    }
    Thread.Sleep(0); // setting at least 1 here would involve a timer which we don't want to
}
public class WinMMWrapper
{
    [DllImport("WinMM.dll", SetLastError = true)]
    public static extern uint timeSetEvent(int msDelay, int msResolution,
        TimerEventHandler handler, ref int userCtx, int eventType);

    public delegate void TimerEventHandler(uint id, uint msg, ref int userCtx,
        int rsv1, int rsv2);

    public enum TimerEventType
    {
        OneTime = 0,
        Repeating = 1
    }

    private readonly Action _elapsedAction;
    private readonly int _elapsedMs;
    private readonly int _resolutionMs;
    private readonly TimerEventType _timerEventType;

    public WinMMWrapper(int elapsedMs, int resolutionMs, TimerEventType timerEventType, Action elapsedAction)
    {
        _elapsedMs = elapsedMs;
        _resolutionMs = resolutionMs;
        _timerEventType = timerEventType;
        _elapsedAction = elapsedAction;
    }

    public uint StartElapsedTimer()
    {
        var myData = 1; //dummy data
        return timeSetEvent(_elapsedMs, _resolutionMs / 10, new TimerEventHandler(TickHandler), ref myData, (int)_timerEventType);
    }

    private void TickHandler(uint id, uint msg, ref int userctx, int rsv1, int rsv2)
    {
        _elapsedAction();
    }
}
class Program
{
    static void Main(string[] args)
    {
        var timer = new WinMMWrapper(100, 25, WinMMWrapper.TimerEventType.Repeating, () =>
        {
            Console.WriteLine($"Timer elapsed {DateTime.UtcNow:o}");
        });

        timer.StartElapsedTimer();

        Console.ReadKey();
    }
}