- - PR -
System.Threading.Timer の終了処理について
投稿者 | 投稿内容 | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2007-05-07 23:06
Tdnr_Symさん、相談に乗って頂けどうもありがとうございます。
>いまさら言うのもなんですが、なんでSystem.Threading.Timer を使われるのでしょう? タイマーの精度を求めるからです。具体的には33msとか16ms毎に描画処理をキックしたい。 私が調べた結果では、.NETで使えるWindowsタイマーには3つあり、精度の悪い順に、 @System.Windows.Forms.Timer 起動スレッドと同じスレッドで動作。55ms程度以上の精度は期待できない ASystem.Threading.Timer 別スレッドにてコールバックをキックしてくれる。精度はそこそこ。 BSystem.Timers.Timer 同じく別スレッドにてコールバックをキック。この3つの中では精度は一番よい という認識です。AはMIDI音楽の再生などに使っている事例もあり、悪くないと判断しました。 >御提示のコードではウィンドウタイマー(System.Windows.Forms.Timer)よりもパフォーマンスがよいとは思えなのですが。 恐れながらパフォーマンスを語られる根拠についてより詳細なご説明を頂ければ幸いです。gcnewですか? 提示したコードはMSDNの同タイマーの使用サンプルコードや、私の一番最初の書き込みに ある@ITのサイトなどで紹介されているもので、こう使うものなのでは?という認識なのですが、 これについても、もしよければより詳細なご説明を頂ければ幸いです。 本当はWin32のマルチメディアタイマーに相当するものを使いたいのですが、MSDNの対応表にも 見当たりません。今はBのタイマーに希望を託しております。 [ メッセージ編集済み 編集者: やっぷ 編集日時 2007-05-07 23:17 ] | ||||||||||||||||||||
|
投稿日時: 2007-05-07 23:35
UIのメッセージ処理の即時性ってどの程度なんですかね? タイマーのコールバックは制度がよくても、その後のUIスレッドへの切り替えや 描画処理は結局UIスレッドでのメッセージ処理になるので、そちらに合わされます。 ※といっても55msよりはよほど正確なのかもしれませんが(私には分かりません) で、元の話ですが、 FormのIsDisposedがfalseでかつIsHandleCreatedがtrueならという条件でInvokeを使用し、 さらにタイミングによる競合にそなえて例外もキャッチする、ってところが現実的ですかね? まあ、Windowsのタイマーなら細かいこと気にしなくてもうまく動くようになっているのかもしれませんが。 少なくともコールバックがUIスレッドと同期するので、正確に確認することが可能ですが、 ThreadingのタイマーなどではコールバックがUIとは同期していないので、 どうしても微妙なタイミングでの競合は発生する可能性があります。 ※つまり、例外を完全に避けるのは多分無理があるでしょう。 --追記 いや、単にlockなどでもいいから競合が発生しないようにうまくやればいけるかな… まあ下手にやると大変なことになる可能性もあるので、きっちり考えないとまずいですが。 [ メッセージ編集済み 編集者: なちゃ 編集日時 2007-05-07 23:39 ] | ||||||||||||||||||||
|
投稿日時: 2007-05-08 00:08
InvokeRequired の存在意義を失わせないであげて下さい。 というか、IsHandleCreated にせよ IsDisposed にせよ、呼び出しに Invoke が必要ですので卵と鶏の関係になってしまいます。 // と言いつつ InvokeRequired の解説には IsHandleCreated が別スレッドからでも呼び出せそうな記述が……。 更に言うと、System.Timers.Timer は SynchronizingObject プロパティによってこの辺の処理を代行してくれます。 [ メッセージ編集済み 編集者: Hongliang 編集日時 2007-05-08 00:10 ] | ||||||||||||||||||||
|
投稿日時: 2007-05-08 00:38
こんばんは。
私が気になったのは次の2点です。 ・Invoke ・Invalidate Control.Invokeはウィンドウの属するスレッドと違うスレッドから呼び出されると、 そのUIのメッセージキューへ専用のメッセージをポストすることで、UIスレッド側へ処理の切り替えを行います。 つまりいくら精度の高いタイマーを使っても、実際にForm1::Invalidate が呼び出されるのはメッセージキューから取り出されるのを待たなくてはなりません。 さらにウィンドウタイマーと違い、下手をするとメッセージキューにいくらでも溜まっていってしまう危険性もありそうです。 もう一つはControl.Invalidateですが引数なしの場合、WindowsAPIのInvalidateRect関数が呼び出されます。 この関数は描画矩形を無効化して再描画を促すだけです。すぐに再描画されるわけではありません。 | ||||||||||||||||||||
|
投稿日時: 2007-05-08 00:39
ドキュメントには記述がないんですが、実際には大丈夫なようです。 がまあ確かに不安がなくはないですね。 InvokeRequiredですが、明確に破棄の判定ではないので余計わかりにくいかなと思いました。Invokeされる側でもチェックが必要になるので。 でもやっぱりドキュメントに明記されてるのでInvokeRequiredの方が安心ですかね… ところでInvokeRequiredはDisposeされた後でも有効なんでしょうか? 結局はどっちもドキュメントでは明記されてない怪しい使い方になるので、シンプルな方を書いたのでした…
これって、どの辺を代行してくれるんでしょう? InvokeRequiredで確認してても、タイミングが悪ければ例外になりそうですが。 その辺もうまくやってくれてるんですかね? --追記 ってちゃんとやってくれてないと問題起こる可能性があるんで、きっとちゃんとやってくれてますね、多分。 --追記 あそうか、今回は必ず別スレッドからになるはずなので、InvokeRequiredがfalseなら それはつまり破棄済みと判断していい、コールバックの本体側も呼ぶ必要はないですね… [ メッセージ編集済み 編集者: なちゃ 編集日時 2007-05-08 02:00 ] | ||||||||||||||||||||
|
投稿日時: 2007-05-08 09:50
InvokeRequired、Invoke、BeginInvoke、EndInvoke、CreateGraphics 以外のすべてののメソッドは別メソッドから呼んじゃいかんと明記されてますよ(プロパティも結局のところメソッド呼び出しですし)。 // もちろん、実際には問題ないものもそれなりにあるでしょうが。完全にマネージドなものとか。
Invoke が必要かどうかの判定なのですからこのプロパティ以外に何を使えと。
確かに、InvokeRequired が Dispose 後も使用可能かどうかは明記されてないですね……。これは一つの弱点です。
適当に ISynchronizeInvoke を実装させて BeginInvoke で例外を投げてみたところ、その例外が握り潰されているようです(.NET 2.0 で確認)。表面的な結果としてはその時の Elapsed イベントは実行されないだけ。 とは言えドキュメントに書いてない以上、例外は念頭に入れておくべきでしょうね。 | ||||||||||||||||||||
|
投稿日時: 2007-05-08 11:09
●みなさんどうもありがとうございました。Threading.Timer を使う限りでは、
結局Blueさんやなちゃさん、その他の方々に提案して頂いたように、 Invokeの出す例外を握りつぶす方法がとりあえずは現実的なのかなぁと思います。 実際これで問題なく正常終了するようにはなりました。(Blueさんのコードに感謝です) ●ただ、Timers.Timer も試してみまして、実験した限りではパフォーマンスも Threading.Timer のそれと少なくとも同等なようであり、本件のような 終了時の問題もなく、今後はこちらを使っていきたいと考えています。 ●Tdnr_Symさん、どうもありがとうございます。 確かにInvalidateだとメッセージキューにポストするだけですね。 どんどん溜まっていってしまう危険性もありますね。 Refresh() というのもあるようなので、このしくみの懸念及び課題として、 また検討したいと思います。 | ||||||||||||||||||||
|
投稿日時: 2007-05-08 13:20
本来は、Invokeが必要かじゃなくて、タイマの本体の処理を実行していいかどうかを知りたいはずですよね?
という意味で書きました。 今回は事実上同じことですが。 |