トップ回答者
Control.EndInvoke は呼ばなくてもよい?

質問
-
Windows Forms アプリで Control.BeginInvoke を使ってメソッドを実行しようとしています。
Control.BeginInvoke のリファレンスでは、末尾の参照のところに同期メソッドの非同期呼び出しへのリンクが張ってあり、このリンク先のページでは、どの手法を使用する場合でも、常に EndInvoke を呼び出して、非同期呼び出しを完了します。
と書いてあります。
しかし、Control.BeginInvokeにもEndInvokeにも、このような注意事項は書いてありません。
Control.EndInvoke は、戻り値を得る必要がなければ呼ばなくてもよいのでしょうか?
αετος(aetos)
Microsoft MVP for Visual C++ Feb 2008 - Jun 2010
http://www.aetosfolia.jp/
http://aetosfolia.spaces.live.com/
Microsoft Certified Technology Specialist
Microsoft Certified Associate
回答
-
Control.BeginInvoke の話でしたね。見落としていました。失礼。
Chris Brumme のブログの中で気になる記事を見つけました。
cbrumme's WebLog : Asynchronous operations, pinning
http://blogs.gotdotnet.com/cbrumme/archive/2003/05/06/51385.aspx
ここの記事とコメントを合わせて読むと新たな発見が。
I just got the official word from the WinForms team. It is not necessary to call Control.EndInvoke. You can call BeginInvoke in a "fire and forget" manner with impunity.
なんて言っています。BeginInvoke しっぱなしでも害はないそうで...。
そういうわけで、前言は撤回いたします。- 回答としてマーク Deleted_ 2009年7月9日 9:19
-
# 古いスレで恐縮ですが
とあるコンサルタントさんから返信を頂いていたことに半年も経ってから気づきましたので引用させて頂きます。
http://blogs.msdn.com/nakama/archive/2009/04/07/part-3-ui.aspx#9901621
Control の場合は BeginInvoke に対する EndInvoke は必須ではありません。というか、デリゲートの非同期実行や非同期 I/O パターンとメソッド名は揃えてありますが、意味が異なるメソッドなので、同一視できないというのが実際のところかと。
αετος(aetos)
Microsoft MVP for Visual C++ Feb 2008 - Jun 2010
http://www.aetosfolia.jp/
http://aetosfolia.spaces.live.com/
Microsoft Certified Technology Specialist
Microsoft Certified Associate
I bing. U bing ?- 回答としてマーク Deleted_ 2010年3月30日 2:15
すべての返信
-
BeginInvoke を呼び出したら、ちゃんと EndInvoke を呼ばないとメモリリークする場合があるはずです。
ちなみに次の URL の記事(VB ですが)にも、
ステップ 7 ハンズオン : 非同期処理を使ったパフォーマンスの向上
http://msdn.microsoft.com/ja-jp/events/dd231287.aspx
Step 6 の中に、
さて、ここで大事な点があります。BeginInvoke で非同期処理を実行して処理が完了したら必ず、EndInvoke を実行しなければならないという事です。
EndInvoke を実行しないとメモリーリークが発生する可能性がありますので、忘れずに実装しましょう。
と書かれています。 -
返信ありがとうございます。
BeginInvoke を呼び出したら、ちゃんと EndInvoke を呼ばないとメモリリークする場合があるはずです。
その根拠を教えて頂きたいのです。
ちなみに次の URL の記事(VB ですが)にも、
ステップ 7 ハンズオン : 非同期処理を使ったパフォーマンスの向上
http://msdn.microsoft.com/ja-jp/events/dd231287.aspx
Step 6 の中に、
さて、ここで大事な点があります。BeginInvoke で非同期処理を実行して処理が完了したら必ず、EndInvoke を実行しなければならないという事です。
EndInvoke を実行しないとメモリーリークが発生する可能性がありますので、忘れずに実装しましょう。
と書かれています。それはデリゲートの非同期実行の方で、MSDNにもEndInvokeを呼ぶべきと明記されています。
Control.BeginInvokeにも同じことが言えるのかどうかを知りたいのです。
αετος(aetos)
Microsoft MVP for Visual C++ Feb 2008 - Jun 2010
http://www.aetosfolia.jp/
http://aetosfolia.spaces.live.com/
Microsoft Certified Technology Specialist
Microsoft Certified Associate -
返信ありがとうございます。
非同期プログラミングの概要 での
BeginOperationName を呼び出すたびに、アプリケーションは EndOperationName も呼び出して、操作の結果を取得する必要があります。
では納得いきませんか?惜しいです。
確かに、Control.BeginInvoke と Control.EndInvoke は、名前だけを見れば Control.Invoke に対する BeginOperationNameと EndOperationName ですし、Begin は IAsyncResult を返し、End はそれを引数に取ります。
ご提示のページで言う「IAsyncResult デザインパターン」に酷似しています。が、そのページには
BeginOperationName メソッド シグネチャには、2 つの追加のパラメータも含まれます。
ともあります。これは Control.BeginInvoke にはあてはまりません。
そのため、このページの内容が Control.BeginInvoke にも適用されるということに、いまいち確信が持てません。Control.Begin/EndInvoke を名指しで書いてある資料があると嬉しいのですが。
αετος(aetos)
Microsoft MVP for Visual C++ Feb 2008 - Jun 2010
http://www.aetosfolia.jp/
http://aetosfolia.spaces.live.com/
Microsoft Certified Technology Specialist
Microsoft Certified Associate -
Control.BeginInvoke の話でしたね。見落としていました。失礼。
Chris Brumme のブログの中で気になる記事を見つけました。
cbrumme's WebLog : Asynchronous operations, pinning
http://blogs.gotdotnet.com/cbrumme/archive/2003/05/06/51385.aspx
ここの記事とコメントを合わせて読むと新たな発見が。
I just got the official word from the WinForms team. It is not necessary to call Control.EndInvoke. You can call BeginInvoke in a "fire and forget" manner with impunity.
なんて言っています。BeginInvoke しっぱなしでも害はないそうで...。
そういうわけで、前言は撤回いたします。- 回答としてマーク Deleted_ 2009年7月9日 9:19
-
返信ありがとうございます。
非常に有力な情報ですね。
αετος(aetos)
Microsoft MVP for Visual C++ Feb 2008 - Jun 2010
http://www.aetosfolia.jp/
http://aetosfolia.spaces.live.com/
Microsoft Certified Technology Specialist
Microsoft Certified Associate -
引数が違うからこのデザインパターンは適用されないという判断はどうかなと思いました。
totojoさんの挙げているブログにもyou should always balance a successful BeginXXX call with its EndXXX counterpart.
と書かれているわけですし、ここで切り逃げをするよりはControl.Invoke()の方がいいと思います。
もちろんControl.Invoke()は完了まで待たされますが、もともとUIを扱っているわけですし、ここで待たされても適切にエラー処理できるならその方がいいですし。
とはいえ、このブログ記事は参考になりますね。 -
返信ありがとうございます。
引数が違うからこのデザインパターンは適用されないという判断はどうかなと思いました。
他にも、MSDN の Control.Begin/EndInvoke のヘルプには呼ぶべしと書かれていない、というのも判断材料の一つではあります。呼ばなくてよいと明記されているわけではないのが疑問の種なのですが。
totojoさんの挙げているブログにも
you should always balance a successful BeginXXX call with its EndXXX counterpart.
と書かれているわけですし、一方で、コメントでは必須ではないとも書かれています。
そのブログ記事はデリゲートあるいは I/O における非同期パターンに主に言及しているのですから、そこで should call なのはまったく自然です。
Control.Invoke がそのパターンに当てはまらないのであれば、当然 should も適用されません。もちろん、EndInvoke を呼べるなら呼んだ方がいいのは承知なのですが、今回、投げっぱなしにしたい事情がありまして。
何が狙いかと言うと、以下のようなことをやっているわけです。- UI スレッドからデリゲートの非同期実行で時間のかかる処理を行う
- ワーカースレッドで発生した例外をイベントで通知する
- イベントハンドラで Control.(Begin)Invoke して、UI スレッドで例外を処理
- 場合によっては例外を再スローして UI スレッドを殺したい
ここで、Control.(End)Invoke を使いますと、UI スレッド内で再スローした例外を捕まえて、ワーカースレッド側に持ってきてくれます。
今回、これが都合が悪いと。ただまぁ、考えてみますと、UI スレッド側にスローするのはデリゲートの EndInvoke がやってくれる(これは今もちゃんと呼んでます)わけで、イベント内で UI スレッドを殺す必要もないんだな、と思いました。
ので、もうちょっと見直してみようと思います。
αετος(aetos)
Microsoft MVP for Visual C++ Feb 2008 - Jun 2010
http://www.aetosfolia.jp/
http://aetosfolia.spaces.live.com/
Microsoft Certified Technology Specialist
Microsoft Certified Associate- 編集済み Deleted_ 2009年7月9日 15:08
-
マイクロソフトのとあるコンサルタントさんのブログ
http://blogs.msdn.com/nakama/archive/2009/04/07/part-3-ui.aspx
でも、Control.BeginInvoke を投げっぱなしにするサンプルは多数掲載されています。
現在、このブログのコメントでも問い合わせしています。
αετος(aetos)
Microsoft MVP for Visual C++ Feb 2008 - Jun 2010
http://www.aetosfolia.jp/
http://aetosfolia.spaces.live.com/
Microsoft Certified Technology Specialist
Microsoft Certified Associate -
ただまぁ、考えてみますと、UI スレッド側にスローするのはデリゲートの EndInvoke がやってくれる(これは今もちゃんと呼んでます)わけで、イベント内で UI スレッドを殺す必要もないんだな、と思いました。
間違いではないですが、自分でもわかりにくかったので補足します。
ので、もうちょっと見直してみようと思います。
Delegate.BeginInvoke にコールバック関数を指定して、その中で Delegate.EndInvoke を呼んでしまうと、コールバック関数はワーカースレッド上で呼ばれるので、例外もワーカースレッド上で発生してしまい、結局、Control.BeginInvoke を投げっぱなしにしないと UI スレッドに例外を持ってくることができません。
が、Delegate.EndInvoke を UI スレッド上で呼び出せばよいですね。
幸い、今回はそこでブロックしても問題ない作りになっています。
αετος(aetos)
Microsoft MVP for Visual C++ Feb 2008 - Jun 2010
http://www.aetosfolia.jp/
http://aetosfolia.spaces.live.com/
Microsoft Certified Technology Specialist
Microsoft Certified Associate -
一方で、コメントでは必須ではないとも書かれています。
そのブログ記事はデリゲートあるいは I/O における非同期パターンに主に言及しているのですから、そこで should call なのはまったく自然です。
Control.Invoke がそのパターンに当てはまらないのであれば、当然 should も適用されません。
もうちょっと調べました。
ControlのInvoke関係の記述にはその他の技術情報として、同期メソッドの非同期呼び出し へのリンクが貼ってあり暗に意識するように注意を促しているようには読み取れます。まぁかなり深読みかもしれませんが、関連があることに間違いはないでしょう。
本題はこっちではなく、Invoke関係はISynchronizeInvokeインターフェイス の実装となります。こちらのドキュメントも確認してみましたが、EndInvoke()を呼ぶようにという記述はありません。Controlのような大きなクラスでの1メソッドとしてなら記載漏れの可能性もありますが、専用interfaceでの仕様の記載漏れという考えにくいので、呼ばなくてもいい根拠の一つにできるかもしれません。
# 仕様として不要だとしても、個人的には対称性を考えて呼ぶべきだという点は変わりませんが。 -
そこがmustとshouldの違いで、呼ばなくてもいいように設計してあるが、呼ぶべきである、ということかと。
いやいやいや。
件のブログ本文では Control.Invoke に言及されていない以上、should ですらない、ということです。ブログの本文を根拠にする限りはね。
αετος(aetos)
Microsoft MVP for Visual C++ Feb 2008 - Jun 2010
http://www.aetosfolia.jp/
http://aetosfolia.spaces.live.com/
Microsoft Certified Technology Specialist
Microsoft Certified Associate -
# 古いスレで恐縮ですが
とあるコンサルタントさんから返信を頂いていたことに半年も経ってから気づきましたので引用させて頂きます。
http://blogs.msdn.com/nakama/archive/2009/04/07/part-3-ui.aspx#9901621
Control の場合は BeginInvoke に対する EndInvoke は必須ではありません。というか、デリゲートの非同期実行や非同期 I/O パターンとメソッド名は揃えてありますが、意味が異なるメソッドなので、同一視できないというのが実際のところかと。
αετος(aetos)
Microsoft MVP for Visual C++ Feb 2008 - Jun 2010
http://www.aetosfolia.jp/
http://aetosfolia.spaces.live.com/
Microsoft Certified Technology Specialist
Microsoft Certified Associate
I bing. U bing ?- 回答としてマーク Deleted_ 2010年3月30日 2:15