3

XXX ミリ秒ごとにスレッドを起動する際に最も正確な .NET オブジェクト (または手法) は何ですか? トレードオフは何ですか?

例えば:

        int maxDurationMs = 1000;
        while (true)
        {
            DateTime dt = DateTime.UtcNow;
            DoQuickStuff()
            TimeSpan duration1 =  DateTime.UtcNow - dt;
            int sleepTime = maxDurationMs - duration1.Milliseconds;
            if (sleepTime > 0)
                System.Threading.Thread.Sleep(sleepTime);
        }

また

       // CPU Intensive, but fairly accurate
       int maxDurationMs = 1000;
        while (true)
        {
            DateTime dt = DateTime.UtcNow;
            DoQuickStuff()
            while (true)
            {
                if (dt.AddMilliseconds(maxDurationMs) >= DateTime.UtcNow)
                    break;
            }
        }

同じことを行う別の方法ですが、精度とトレードオフの程度が異なります (CPU など)。

4

5 に答える 5

4

Don't use DateTime: its accuracy is limited to around 16ms on most systems. (See Eric Lippert's blog)

The most accurate method would be to have a dedicated thread running a while loop with a System.Diagnostics.Stopwatch object to count the time.

Even with the most precise and accurate timer in existance, raising an event exactly every x milliseconds is no simple task given the unpredictability of CPU time slices: I suggest looking into how games do their main loop (achieving a stable 30fps with lag compensation, for instance). A good example is OpenTK's GameWindow, specifically the RaiseUpdateFrame method.

于 2012-05-16T15:59:41.587 に答える
4

私は実際にそれらを自分で使用したことはありませんが、マルチメディア タイマーは、Windows のタイマー サービスの中で最高の解像度を持つと言われています。.NET BCL にはこのタイマー サービスのラッパーがまだないため、自分で P/Invoke 呼び出しを行う必要があります。

別のオプションは、タイトなループでStopwatchいくつかの標準呼び出しと一緒に使用することです。Thread.Sleepこのアプローチでどれだけの幸運が得られるかはわかりませんが、単純な古いThread.Sleep呼び出し自体よりも正確かもしれません。試したことはありませんが、試してみる価値のあるものは何でもあると思います。

いくつかの実験をThreadPriority.Highest行ったところ、スレッドの優先度を に変更するとかなりの違いが生じることがわかりました。私が試した各手法で、間隔の標準偏差がかなり減少しました。

于 2012-05-16T02:29:35.510 に答える
2

正確な間隔が必要な場合は、おそらく Windows タイマーを使用する必要はありません。おそらく、ある種のRTOSの方が適しているでしょう。

上記のリンクから:

タイマー API は、現在利用可能なタイマーの問題に対処するために作成されました... ただし、Windows タイマーは、アプリケーションが必要とするほど正確ではありません。Windows タイマー メッセージはミリ秒単位の精度でスケジュールできますが、Windows タイマーの精度はシステム クロックと現在のアクティビティに依存するため、その結果が得られることはめったにありません。WM_TIMER メッセージは、WM_PAINT メッセージと同様に低い優先度で処理されるため、他のメッセージが処理されている間、遅延が発生することがよくあります。

于 2012-05-15T22:56:11.397 に答える
2

私のウィンドウサービスでは、定期的なアクションを使用Monitor.Waitしています。これは、スレッドを解放し、終了する前に次の「タイマーティック」を気にせずにアクションを実行できるためです。これにより、+/- 1 ミリ秒の精度が得られます。すべてがうまくいけば。

しかし、信頼できる完璧な精度が必要な場合は、.NET を使用しないでください。実際には、Windows を使用するべきではありません。プロセス (またはスレッド) の実行が延期される可能性は常にあります。

于 2012-05-15T23:12:47.997 に答える
0

System.Timers.Timerオブジェクトの基本的な実装では、約120 MSが歪んでおり、毎分少なくとも1秒スキップしていました。

次の手法を使用して、1分間隔で1ミリ秒以内に正確なタイマーを取得できます。間隔を短くすると、同じ精度を達成できない場合があります(さらに、DoWork()のオーバーヘッドがこの効率に影響します)

 public class SystemTimerTest
   {

    readonly System.Timers.Timer timerRecalcStatistics;
    readonly System.Diagnostics.Stopwatch stopwatchForRecalcStatistics = new System.Diagnostics.Stopwatch();


    public SystemTimerTest(TimeSpan range, DataOverwriteAction action)
    {
        int recalculateStatisticsEveryXMillseconds = 1000;

        timerRecalcStatistics = new System.Timers.Timer(recalculateStatisticsEveryXMillseconds);
        timerRecalcStatistics.AutoReset = true;
        timerRecalcStatistics.Elapsed += new System.Timers.ElapsedEventHandler(TimerRecalcStatisticsElapsed);
        timerRecalcStatistics.Interval = recalculateStatisticsEveryXMillseconds;
        timerRecalcStatistics.Enabled = true;


        this.maxRange = range;
        this.hashRunningTotalDB = new HashRunningTotalDB(action);
        this.hashesByDate = new HashesByDate(action);
        this.dataOverwriteAction = action;
    }


    private void TimerRecalcStatisticsElapsed(object source, System.Timers.ElapsedEventArgs e)
    {
        stopwatchForRecalcStatistics.Start();
        Console.WriteLine("The TimerRecalcStatisticsElapsed event was raised at {0}", e.SignalTime.ToString("o"));

         // DO WORK HERE


        stopwatchForRecalcStatistics.Stop();
        double timeBuffer  = GetInterval(IntervalTypeEnum.NearestSecond, e.SignalTime) - stopwatchForRecalcStatistics.ElapsedMilliseconds;

        if (timeBuffer > 0)
            timerRecalcStatistics.Interval = timeBuffer;
        else
            timerRecalcStatistics.Interval = 1;

        stopwatchForRecalcStatistics.Reset();         
        timerRecalcStatistics.Enabled = true;
    }
 }

これが1秒サイクルあたり1〜120ミリ秒を失ったということは、CPUがこの実装の場合ほど効率的ではないことを意味するのではないかと思います。

于 2012-05-17T20:01:07.080 に答える