Самый точный таймер в .NET?

Выполнение следующего (слегка псевдокода) дает следующие результаты. Я в шоке от того, насколько точен таймер (увеличение ~ 14 мс каждый Tick ).

Есть ли что-нибудь более точное?

void Main()
{
   var timer = new System.Threading.Timer(TimerCallback, null, 0, 1000);
}

void TimerCallback(object state)
{
   Debug.WriteLine(DateTime.Now.ToString("ss.ffff"));
}

Sample Output:
...
11.9109
12.9190
13.9331
14.9491
15.9632
16.9752
17.9893
19.0043
20.0164
21.0305
22.0445
23.0586
24.0726
25.0867
26.1008
27.1148
28.1289
29.1429
30.1570
31.1710
32.1851
30
задан maxp 10 February 2012 в 13:11
поделиться

3 ответа

Я также был свидетелем класса с точностью до 1 мс. Я взял код Ханса Пассанта с форума
https://social.msdn.microsoft.com/Forums/en-US/6cd5d9e3-e01a-49c4-9976-6c6a2f16ad57/1-millisecond-timer
и обернули его в класс для простоты использования в вашей форме. Вы можете легко настроить несколько таймеров, если хотите. В приведенном ниже примере кода я использовал 2 таймера. Я проверил это, и он работает нормально.

// AccurateTimer.cs
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace YourProjectsNamespace
{
    class AccurateTimer
    {
        private delegate void TimerEventDel(int id, int msg, IntPtr user, int dw1, int dw2);
        private const int TIME_PERIODIC = 1;
        private const int EVENT_TYPE = TIME_PERIODIC;// + 0x100;  // TIME_KILL_SYNCHRONOUS causes a hang ?!
        [DllImport("winmm.dll")]
        private static extern int timeBeginPeriod(int msec);
        [DllImport("winmm.dll")]
        private static extern int timeEndPeriod(int msec);
        [DllImport("winmm.dll")]
        private static extern int timeSetEvent(int delay, int resolution, TimerEventDel handler, IntPtr user, int eventType);
        [DllImport("winmm.dll")]
        private static extern int timeKillEvent(int id);

        Action mAction;
        Form mForm;
        private int mTimerId;
        private TimerEventDel mHandler;  // NOTE: declare at class scope so garbage collector doesn't release it!!!

        public AccurateTimer(Form form,Action action,int delay)
        {
            mAction = action;
            mForm = form;
            timeBeginPeriod(1);
            mHandler = new TimerEventDel(TimerCallback);
            mTimerId = timeSetEvent(delay, 0, mHandler, IntPtr.Zero, EVENT_TYPE);
        }

        public void Stop()
        {
            int err = timeKillEvent(mTimerId);
            timeEndPeriod(1);
            System.Threading.Thread.Sleep(100);// Ensure callbacks are drained
        }

        private void TimerCallback(int id, int msg, IntPtr user, int dw1, int dw2)
        {
            if (mTimerId != 0)
                mForm.BeginInvoke(mAction);
        }
    }
}

// FormMain.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace YourProjectsNamespace
{
    public partial class FormMain : Form
    {
        AccurateTimer mTimer1,mTimer2;

        public FormMain()
        {
            InitializeComponent();
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            int delay = 10;   // In milliseconds. 10 = 1/100th second.
            mTimer1 = new AccurateTimer(this, new Action(TimerTick1),delay);
            delay = 100;      // 100 = 1/10th second.
            mTimer2 = new AccurateTimer(this, new Action(TimerTick2), delay);
        }

        private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            mTimer1.Stop();
            mTimer2.Stop();
        }

        private void TimerTick1()
        {
            // Put your first timer code here!
        }

        private void TimerTick2()
        {
            // Put your second timer code here!
        }
    }
}
14
ответ дан 27 November 2019 в 23:56
поделиться

Это на самом деле не делает таймер более точным (как в , оно не гарантирует, что время между обратными вызовами составляет ровно 1 секунду ), но если все, что вам нужно, это таймер, который срабатывает один раз каждые секунды и не пропускают секунды из-за проблемы дрейфа ~14ms (как показано в примере вывода OP между 17-й и 19-й секундами), вы можете просто изменить таймер на запуск в наступающей секунде, как только будет выполнен обратный вызов пожары (и, очевидно, вы могли бы сделать то же самое для наступающей минуты, наступающего часа и т. д., если все, что вам нужно, это убедиться, что интервал не смещается):

using System.Threading;

static Timer timer;

void Main()
{   
    // 1000 - DateTime.UtcNow.Millisecond = number of milliseconds until the next second
    timer = new Timer(TimerCallback, null, 1000 - DateTime.UtcNow.Millisecond, 0);
}

void TimerCallback(object state)
{   
    // Important to do this before you do anything else in the callback
    timer.Change(1000 - DateTime.UtcNow.Millisecond, 0);

    Debug.WriteLine(DateTime.UtcNow.ToString("ss.ffff"));
}

Sample Output:
...
25.0135
26.0111
27.0134
28.0131
29.0117
30.0135
31.0127
32.0104
33.0158
34.0113
35.0129
36.0117
37.0127
38.0101
39.0125
40.0108
41.0156
42.0110
43.0141
44.0100
45.0149
46.0110
47.0127
48.0109
49.0156
50.0096
51.0166
52.0009
53.0111
54.0126
55.0116
56.0128
57.0110
58.0129
59.0120
00.0106
01.0149
02.0107
03.0136
1
ответ дан 27 November 2019 в 23:56
поделиться

Это не таймер, который является неточным, но DateTime.Now , который имеет объявленный допуск 16 мс.

Вместо этого я бы использовал свойство Environment.Ticks для измерения циклов ЦП во время этого теста.

Редактировать : Environment.Ticks также основан на системном таймере и может иметь те же проблемы с точностью, что и DateTime.Now. Я бы посоветовал выбрать StopWatch вместо этого, как упоминали многие другие ответчики.

3
ответ дан 27 November 2019 в 23:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: