Таймер высокого разрешения в C #

c# timer high-resolution

10724 просмотра

3 ответа

Есть ли таймер высокого разрешения, который вызывает событие каждый раз, когда таймер истекает, как System.Timerкласс? Мне нужен таймер высокого разрешения для Elapseкаждой мс.

Я продолжаю сталкиваться с сообщениями, которые объясняют, что секундомер может измерять высокое разрешение, но я не хочу измерять время, я хочу создать интервал в 1 мс.

Что-то есть в .NET или я собираюсь написать свой собственный таймер высокого разрешения?

Автор: bas Источник Размещён: 12.11.2019 09:39

Ответы (3)


12 плюса

Решение

Там нет ничего встроенного в .NET Framework, о котором я знаю. В Windows есть механизм для событий таймера высокого разрешения через API мультимедийного таймера . Ниже приведен краткий пример, который я, похоже, выполнил. Есть также , кажется, быть хорошим примером здесь .

Отмечу, что этот API изменяет общесистемные настройки, которые могут ухудшить производительность системы, поэтому покупатель остерегается. В целях тестирования я бы порекомендовал отслеживать частоту срабатывания таймера, чтобы убедиться, что синхронизация аналогична устройству, которое вы пытаетесь смоделировать. Поскольку Windows не является операционной системой реального времени, загрузка вашей системы может привести к задержке таймера MM, что приведет к промежуткам в 100 мс, которые содержат 100 событий в быстрой последовательности, а не к 100 событиям, разнесенным на 1 мс. Некоторое дополнительное чтение на таймерах 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);
}
Автор: Mike Zboray Размещён: 19.07.2014 07:05

0 плюса

Попробуйте создать новое 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();
Автор: user3855678 Размещён: 19.07.2014 10:29

0 плюса

Есть вариант: использовать Thread.Sleep(0). Попытка вызвать Thread.Sleep(1)или нанять вызов System.Threading.Timerвсегда сводится к разрешению системного таймера. В зависимости от одного, вероятно, не самая лучшая идея, в конце дня вашему приложению может быть просто запрещено звонить timeBeginPeriod(...)из winmm.dll.

Следующий код может разрешиться до +/- 10 нс (0,10 мс) на моей машине разработчика (i7q) и может быть выше. Это приведет к серьезной нагрузке на одно из ядер вашего процессора, что увеличит его использование до 100%. Никакого реального замедления ОС не произойдет, код отдает большую часть своего времени процессора, вызывая Thread.Sleep как можно раньше:

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
}

Для более полной реализации см. Мой другой ответ

Автор: Vitaly Размещён: 03.04.2019 08:16
Вопросы из категории :
32x32