- - PR -
BeginInvokeで実行した処理の中断
1
投稿者 | 投稿内容 | ||||
---|---|---|---|---|---|
|
投稿日時: 2006-05-13 18:22
VB.NET(VS2003)で開発をしています。
BeginInvokeで実行した処理の中断について疑問があります。 ご教示ください。 以下ソースです(新規プロジェクトでForm2を追加し、Form1にはButtonを1個追加)。 ---------------- ■Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim f As Form2 Try f = New Form2 f.ShowDialog(Me) Finally If Not f Is Nothing Then f.Dispose() End If End Try End Sub ■Form2 Delegate Sub TestMethodDelegate() Private _ar As IAsyncResult Private _testMethodDelegateInstance As TestMethodDelegate Private _abort As Boolean = False Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load _testMethodDelegateInstance = New TestMethodDelegate(AddressOf TestMethod) _ar = _testMethodDelegateInstance.BeginInvoke(New AsyncCallback(AddressOf TestMethodCallback), Nothing) End Sub Private Sub TestMethod() Dim i As Integer = 0 Do While _abort = False And i < 10 Console.WriteLine(i) i += 1 System.Threading.Thread.Sleep(10000) Loop End Sub Private Sub TestMethodCallback(ByVal ar As IAsyncResult) _testMethodDelegateInstance.EndInvoke(ar) Console.WriteLine("終了") End Sub Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing _abort = True End Sub ---------------- ・Button1がクリックされたらForm2を表示 ・Form2ではLoad時にBeginInvokeによりバックグラウンドで処理(TestMethod)を開始 ・TestMethodの処理が終了したらコールバックメソッド(TestMethodCallback)によりConsoleに「終了」を出力 ・Form2が閉じられたらTestMethodの処理を中断 ということをしています。 中断は_abortフラグで判断するようにしましたが、ここで疑問が沸きました。 TestMethodが終了してからForm2を閉じられる分には問題ないと思うのですが、 TestMethodの終了前に閉じられた場合、TestMethodCallbackが呼ばれる前に Form2のインスタンスが破棄されるように思います。 ですが、実際にはForm1でf.Dispose()が呼び出された後に「終了」がきちんと 表示されます。 この動きはどう理解すればいいのでしょうか? 「たまたま動いてしまっている」のであれば、どう実装すべきでしょうか? よろしくお願いします。 | ||||
|
投稿日時: 2006-05-13 22:19
どんな点を疑問に思っていますか? フォームが破棄された後だと、コールバックが呼び出されないはず、と思っていますか? フォームが破棄された後だと、Consoleへの書き込みはできないはず、と思っていますか? あるいは、フォームが破棄された後だと、フォームのメソッドは実行できないはず、だと思っていますか? | ||||
|
投稿日時: 2006-05-14 00:20
この点が疑問です。 Form2のインスタンスがどこからも参照されなくなったら、どこかのタイミングでガベージコレクタに回収される…という理解は合っていますよね? とすると、うまく動いている理由を考えると、 ・回収される前にTestMethod終了→TestMethodCallbackの処理が行われて、たまたまうまく動いてしまっている? ・BeginInvokeによる実行は、実は誰かがForm2のインスタンスの参照を保持しているので、TestMethodCallbackが呼び出されるまでは回収はされない? のどちらかなんじゃないかな…?と思っていますが、ここで詰まってしまいました。 | ||||
|
投稿日時: 2006-05-14 01:40
TestMethodCallbackはデリゲートとして渡しています。 このデリゲートはコールバックで必要ですから、BeginInvokeの処理の過程で内部的に保持されているでしょう。 デリゲートのインスタンス(への参照)が生きていますから、呼び出し対象のメソッドの属するインスタンスへの参照も生きています。 ※こういうことが知りたい? | ||||
|
投稿日時: 2006-05-14 10:17
なるほど…TestMethodCallbackへの参照を保持していなければ呼び出しはできませんよね。 でも、それでもやっぱり回収されてしまう可能性があるのでは…という疑問があります。 ガベージコレクションの考え方ですが、 ----- ObjMainがObjAを作成し、ObjAがObjBを作成する。 この時ObjBにはObjAへの参照を渡している(ObjAとObjBは循環参照になる)。 ここでObjMainがObjAへの参照を解放したら、ObjAとObjBは宙ぶらりんの状態になり、やがてガベージコレクタに回収される(.NETのガベージコレクタが循環参照を検知して回収する)。 ----- と理解しています。 示したコードもこれと同じ構図になるように思える(ObjMain=Form1/ObjA=Form2/ObjB=_testMethodDelegateInstance?)のですが…。 だとすると、やはりたまたま動いているだけなのでは?と思ってしまいます。 | ||||
|
投稿日時: 2006-05-15 20:10
と参照がなくなる時点に、GCにてCollectされるでしょうか? 実際、GC.Collect()は参照がなくなったら、すぐに動作するのではなく、明示的に行わない限り、必要な場合のみ行われます。 後、BeginInvokeで起したThreadは実にThreadPoolに管理されており、自分のアプリ側にCallbackへの参照を破棄しても、ThreadPoolの管理Threadはちゃんともっていると思います。 | ||||
|
投稿日時: 2006-05-16 20:15
参照が無くなった後にGC.Collect()してみましたが、Callbackも呼ばれて問題なく動作しました。 引き続きThreadPool等についても調べてみようと思います。 なちゃさん、Bobさん、ありがとうございました。 |
1