2023年9月25日月曜日

C#のTimer精度と挙動

Timerの精度云々

 C#のTimerはSystem.Timersのもの、System.Threadingのもの、GUI用のもの(Forms, WPF)があって云々とかは特にここでは触れません。とりあえずUI用じゃなければTimersのものを使っときましょう。
 また、いずれのタイマーも精度はよろしくありません。実測では15ms程度の分解能しかありませんでした。本当に精度が欲しいならwhileループでStopwatchの監視するのが一番手っ取り早いと思います。
 以下では意外と見向きされないTimerの特徴を実測で調べたので、Q&A形式で紹介していきます。なおタイマーはフリーラン状態(AutoReset==true)とします。実測環境は.NET6.0です。

塵も積もれば山となる?

 上記のようにTimerの精度はよろしくありません。ではノンストップでタイマーを動かし続けた場合、累積誤差となって現れるのでしょうか?

Q. 100ミリ秒間隔で1000回イベントを発生させると何秒かかる?

 

 

 

A. ほぼ100000ミリ秒(±10ミリ秒程度)

 実は誤差は累積しません内部的に帳尻合わせしてくれるので、何万回目のイベントだろうとトータルの誤差として1分解能以下のズレしか現れないようです。TimersもThreadingも同じです。
 ただし分解能より短いイベント間隔だと流石に誤差がたまっていきます。(例:10ミリ秒間隔)


時間のかかる処理

 定期的にElapsedイベントで処理をしていて、まれにインターバルの時間よりも長い処理時間がかかってしまう場合を想定します。どうなるでしょう?
 

Q. 100ミリ秒間隔でイベントを発生させ、10回目のイベントにおいて処理に150ミリ秒かかってしまった。11回目のイベントはどうなる?

① 例外が発生する

② 11回目のイベント発生時に10回目の処理が強制中止される

③ 10回目の処理が終わり次第11回目が発生する

④ 11回目のイベント発生がキャンセルされ、実質12回目が11回目となる

⑤ 10回目の処理完了を待たず、11回目が並列実行される

⑥ 10回目の処理完了100ミリ秒後に11回目が発生する

 
 
 

 A. ⑤ 並列実行される

 Timerのイベントは毎回同一スレッドで起こるとは限りません。同じスレッドで発生することが多いようですが、たとえ一瞬で処理し終える内容しかなくてもThreadIDがたまに切り替わっていることが確認できます。ゆえに前回のElapsedイベントが発生したスレッドがまだ解放されていない場合、今回のElapsedイベントは別の暇してるスレッドに処理をあてがいます。これもTimers、Threadingともに該当します。

0 件のコメント:

コメントを投稿