結論を先に

繰り返したい処理を記述したメソッドをDispatcher.InvokeAsyncで呼び出し、そのメソッドの中で自らをDispatcher.InvokeAsyncする。
DispatcherPriorityは繰り返したい処理の優先度や頻度に応じて適切に設定する。マウス操作を妨げる可能性がある、妨げてはならないならBackGroundより低くするべきである。
繰り返したい処理が短いものであって、繰り返し間隔が十数ミリ秒を超えて(毎秒の繰り返し回数が50回程度までで)良ければ、メソッドにThread.Sleep(1)を加えておく。これでCPU時間を消費せずに済む。
背景
長時間を要する処理を、できればシングルスレッドで記述したい。マルチスレッドの複雑さを避けたいからである。
あるいは、別スレッド上での進捗を定期的にGUIスレッドで監視・表示・制御する「繰り返し」処理が必要なこともある。
何らかのタイマーを使っても良いが間隔の適切な設定が面倒である。長すぎて無駄(無用な待ち時間が生じる)だったり、短すぎて無駄(何も処理することがない呼び出しが多数発生する)だったり。
CPU時間をほとんど消費せず、短く処理を済ませては「自らを呼び出して」待機し、すぐまた呼び出してもらえる方法が欲しい。
解決
WPFであれば、システムに備わるメッセージ・キューの機能を.Net Frameworkや.Netの機能として呼び出すことができる。DispatcherのInvokeAsyncのメソッドによる。
これは本来GUIの操作を別スレッドからの要求に応じて行うためのものである。言い換えると、GUIスレッド上でのメソッドの実行を別スレッドから呼び出す機構である。さらに言い換えると、GUIスレッドで処理すべきことが「メッセージ」としてキューに蓄えられる中に、メソッド呼び出しの「メッセージ」も登録する機能である。これにより、キーボードやマウスによる操作が「メッセージ」として送られGUIスレッド上でイベントが発生する(メソッドが呼び出される)ことと同様に、特定のメソッドの呼び出しも登録し順次実行することが可能である。
DispatcherにはInvokeメソッドもあるが、これは他のスレッドで用いるもので、GUIスレッドでメソッド実行が完了するまで待機する。本件はGUIスレッド上で使うこと、次回処理の「メッセージ」を登録だけすることを意図しているのでInvokeAsyncを使う。
繰り返しをプロセスやスレッドの切り替え無しに行う場合、CPU時間を必ず消費する。プロセスやスレッドの切り替えを伴ってよければ、Thread.Sleepを用いることでCPU時間の消費を避けられる。しかし、プロセスやスレッドを切り替えるタイミングはシステムタイマーに依存しており、タイマーの解像度十数ミリ秒が繰り返し間隔の下限となる。
改訂日 | 内 容 |
---|---|
2025/3/1 | BeginInvokeは古いメソッド。新しいInvokeAsyncを使うべきなので修正。 |