- PR -

System.Threading.Timer の終了処理について

投稿者投稿内容
やっぷ
会議室デビュー日: 2007/05/07
投稿数: 17
投稿日時: 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 ]
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2007-05-07 23:35
引用:

やっぷさんの書き込み (2007-05-07 23:06) より:

タイマーの精度を求めるからです。具体的には33msとか16ms毎に描画処理をキックしたい。
私が調べた結果では、.NETで使えるWindowsタイマーには3つあり、精度の悪い順に、
@System.Windows.Forms.Timer 起動スレッドと同じスレッドで動作。55ms程度以上の精度は期待できない
ASystem.Threading.Timer 別スレッドにてコールバックをキックしてくれる。精度はそこそこ。
BSystem.Timers.Timer 同じく別スレッドにてコールバックをキック。この3つの中では精度は一番よい
という認識です。AはMIDI音楽の再生などに使っている事例もあり、悪くないと判断しました。


UIのメッセージ処理の即時性ってどの程度なんですかね?
タイマーのコールバックは制度がよくても、その後のUIスレッドへの切り替えや
描画処理は結局UIスレッドでのメッセージ処理になるので、そちらに合わされます。
※といっても55msよりはよほど正確なのかもしれませんが(私には分かりません)

で、元の話ですが、
FormのIsDisposedがfalseでかつIsHandleCreatedがtrueならという条件でInvokeを使用し、
さらにタイミングによる競合にそなえて例外もキャッチする、ってところが現実的ですかね?

まあ、Windowsのタイマーなら細かいこと気にしなくてもうまく動くようになっているのかもしれませんが。
少なくともコールバックがUIスレッドと同期するので、正確に確認することが可能ですが、
ThreadingのタイマーなどではコールバックがUIとは同期していないので、
どうしても微妙なタイミングでの競合は発生する可能性があります。
※つまり、例外を完全に避けるのは多分無理があるでしょう。

--追記
いや、単にlockなどでもいいから競合が発生しないようにうまくやればいけるかな…
まあ下手にやると大変なことになる可能性もあるので、きっちり考えないとまずいですが。


[ メッセージ編集済み 編集者: なちゃ 編集日時 2007-05-07 23:39 ]
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2007-05-08 00:08
引用:

なちゃさんの書き込み (2007-05-07 23:35) より:
FormのIsDisposedがfalseでかつIsHandleCreatedがtrueならという条件でInvokeを使用し、


InvokeRequired の存在意義を失わせないであげて下さい。
というか、IsHandleCreated にせよ IsDisposed にせよ、呼び出しに Invoke が必要ですので卵と鶏の関係になってしまいます。
// と言いつつ InvokeRequired の解説には IsHandleCreated が別スレッドからでも呼び出せそうな記述が……。

更に言うと、System.Timers.Timer は SynchronizingObject プロパティによってこの辺の処理を代行してくれます。

[ メッセージ編集済み 編集者: Hongliang 編集日時 2007-05-08 00:10 ]
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2007-05-08 00:38
こんばんは。

引用:

やっぷさんの書き込み (2007-05-07 23:06) より:
>御提示のコードではウィンドウタイマー(System.Windows.Forms.Timer)よりもパフォーマンスがよいとは思えなのですが。
恐れながらパフォーマンスを語られる根拠についてより詳細なご説明を頂ければ幸いです。gcnewですか?



私が気になったのは次の2点です。
・Invoke
・Invalidate

Control.Invokeはウィンドウの属するスレッドと違うスレッドから呼び出されると、
そのUIのメッセージキューへ専用のメッセージをポストすることで、UIスレッド側へ処理の切り替えを行います。
つまりいくら精度の高いタイマーを使っても、実際にForm1::Invalidate が呼び出されるのはメッセージキューから取り出されるのを待たなくてはなりません。
さらにウィンドウタイマーと違い、下手をするとメッセージキューにいくらでも溜まっていってしまう危険性もありそうです。

もう一つはControl.Invalidateですが引数なしの場合、WindowsAPIのInvalidateRect関数が呼び出されます。
この関数は描画矩形を無効化して再描画を促すだけです。すぐに再描画されるわけではありません。

なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2007-05-08 00:39
引用:

Hongliangさんの書き込み (2007-05-08 00:08) より:
InvokeRequired の存在意義を失わせないであげて下さい。
というか、IsHandleCreated にせよ IsDisposed にせよ、呼び出しに Invoke が必要ですので卵と鶏の関係になってしまいます。
// と言いつつ InvokeRequired の解説には IsHandleCreated が別スレッドからでも呼び出せそうな記述が……。


ドキュメントには記述がないんですが、実際には大丈夫なようです。
がまあ確かに不安がなくはないですね。
InvokeRequiredですが、明確に破棄の判定ではないので余計わかりにくいかなと思いました。Invokeされる側でもチェックが必要になるので。
でもやっぱりドキュメントに明記されてるのでInvokeRequiredの方が安心ですかね…

ところでInvokeRequiredはDisposeされた後でも有効なんでしょうか?
結局はどっちもドキュメントでは明記されてない怪しい使い方になるので、シンプルな方を書いたのでした…

引用:

更に言うと、System.Timers.Timer は SynchronizingObject プロパティによってこの辺の処理を代行してくれます。


これって、どの辺を代行してくれるんでしょう?
InvokeRequiredで確認してても、タイミングが悪ければ例外になりそうですが。
その辺もうまくやってくれてるんですかね?

--追記
ってちゃんとやってくれてないと問題起こる可能性があるんで、きっとちゃんとやってくれてますね、多分。

--追記
あそうか、今回は必ず別スレッドからになるはずなので、InvokeRequiredがfalseなら
それはつまり破棄済みと判断していい、コールバックの本体側も呼ぶ必要はないですね…



[ メッセージ編集済み 編集者: なちゃ 編集日時 2007-05-08 02:00 ]
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2007-05-08 09:50
引用:

なちゃさんの書き込み (2007-05-08 00:39) より:
ドキュメントには記述がないんですが、実際には大丈夫なようです。
がまあ確かに不安がなくはないですね。


InvokeRequired、Invoke、BeginInvoke、EndInvoke、CreateGraphics 以外のすべてののメソッドは別メソッドから呼んじゃいかんと明記されてますよ(プロパティも結局のところメソッド呼び出しですし)。
// もちろん、実際には問題ないものもそれなりにあるでしょうが。完全にマネージドなものとか。

引用:

InvokeRequiredですが、明確に破棄の判定ではないので余計わかりにくいかなと思いました。Invokeされる側でもチェックが必要になるので。
でもやっぱりドキュメントに明記されてるのでInvokeRequiredの方が安心ですかね…


Invoke が必要かどうかの判定なのですからこのプロパティ以外に何を使えと。

引用:

ところでInvokeRequiredはDisposeされた後でも有効なんでしょうか?
結局はどっちもドキュメントでは明記されてない怪しい使い方になるので、シンプルな方を書いたのでした…


確かに、InvokeRequired が Dispose 後も使用可能かどうかは明記されてないですね……。これは一つの弱点です。

引用:

引用:

更に言うと、System.Timers.Timer は SynchronizingObject プロパティによってこの辺の処理を代行してくれます。


これって、どの辺を代行してくれるんでしょう?
InvokeRequiredで確認してても、タイミングが悪ければ例外になりそうですが。
その辺もうまくやってくれてるんですかね?

--追記
ってちゃんとやってくれてないと問題起こる可能性があるんで、きっとちゃんとやってくれてますね、多分。


適当に ISynchronizeInvoke を実装させて BeginInvoke で例外を投げてみたところ、その例外が握り潰されているようです(.NET 2.0 で確認)。表面的な結果としてはその時の Elapsed イベントは実行されないだけ。
とは言えドキュメントに書いてない以上、例外は念頭に入れておくべきでしょうね。
やっぷ
会議室デビュー日: 2007/05/07
投稿数: 17
投稿日時: 2007-05-08 11:09
●みなさんどうもありがとうございました。Threading.Timer を使う限りでは、
 結局Blueさんやなちゃさん、その他の方々に提案して頂いたように、
 Invokeの出す例外を握りつぶす方法がとりあえずは現実的なのかなぁと思います。
 実際これで問題なく正常終了するようにはなりました。(Blueさんのコードに感謝です)

●ただ、Timers.Timer も試してみまして、実験した限りではパフォーマンスも
 Threading.Timer のそれと少なくとも同等なようであり、本件のような
 終了時の問題もなく、今後はこちらを使っていきたいと考えています。

●Tdnr_Symさん、どうもありがとうございます。
 確かにInvalidateだとメッセージキューにポストするだけですね。
 どんどん溜まっていってしまう危険性もありますね。
 Refresh() というのもあるようなので、このしくみの懸念及び課題として、
 また検討したいと思います。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2007-05-08 13:20
本来は、Invokeが必要かじゃなくて、タイマの本体の処理を実行していいかどうかを知りたいはずですよね?
という意味で書きました。
今回は事実上同じことですが。

スキルアップ/キャリアアップ(JOB@IT)