タイトルが長くなった。
ちなみに検証できないので内容的に間違っているかもしれません。
解決方法も最善解ではないです。
完全に自分の覚え書き用の記事です。
1 2 3 4 5 6 7 8 9 10 11 |
SampleForm form=new SampleForm(); public void Reflesh() { // (中略1) form.Invoke(new MethodInvoker(form.Close)); // (中略2) form=new SampleForm(); // (中略3) form.Show(); } |
おおまかに言うと、上記のようなコードを実装していた。
InvokeしてるのはこのメソッドがUIスレッドでない別スレッドで実行されるからです。
簡単に言うと「今表示されているSampleFormを閉じて、初期化して再度表示する」という実行内容。
で、これがいざ実行するとハングすることがある。
困ったのは、100%でなく、起こったり起こらなかったりするところである。
form.Show()の直前でブレイクしてデバッグすると、どうやらここでformがDisposeされていることがあるということが分かった。
しかし、// (中略2)や// (中略3)のどこにもformをDisposeする要素がない。
より詳細に見ようと、// (中略2)にブレイクポイントを置いてデバッグしたところ、問題が一切発生しなくなった。
しばらく頭を捻っていたが、ようやく尤もらしい仮説が見つかった。
InvokeされたCloseによるDispose処理が、「form=new SampleForm()」後に実行され、
折角初期化したformがDisposeされてしまった。
シナリオはこうだ。
1)(処理スレッド)CloseをUIスレッドに託す
2)(UIスレッド)formをCloseする
3)(処理スレッド)処理スレッドformを初期化する
4)(UIスレッド)Closeしたことだし、formをDisposeする
5)(処理スレッド)formをShowする
↓
formはDisposeされてるから無理ですよ〜だ。
4が3と5の間に起こると本件が発生する。
そのタイミングは環境の状態によるので、問題が発生したりしなかったりした訳だ。
で、以下のように1行追加して問題は発生しなくなった。
1 2 3 |
form.Invoke(new MethodInvoker(form.Close)); Application.DoEvents(); // (中略2) |
一度たまっている処理(Close含む、ってか多分Closeしかない)を全て実行させればいいのだ。
Mutexを使う等して排他制御をした方が正確だが、バグの温床に成りかねないのでこの方法にした。
ちょっと不安ではあるけど、これで問題なさそうならこれで行こうと思う。
昔「おまじないだ」と言われたApplication.DoEvents()を使うことになるとはなぁ。
ネットで調べたらあまり勧められない方法らしいけど、そこまで必死に直す箇所じゃないからね。仕方ないね。