C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[17]: Control.Invokeが使えない件。 [2]


(過去ログ 18 を表示中)

[トピック内 52 記事 (41 - 52 表示)]  << 0 | 1 | 2 >>

■6960 / inTopicNo.41)  Re[13]: Control.Invokeが使えない件。
  
□投稿者/ えムナウ (87回)-(2007/08/28(Tue) 06:01:52)
今回のコードではForm2を実行しているスレッドでは Me.Text = text を通る前に FormClosingイベントを通っていないことが担保されます。
いかがでしょうか?

引用返信 編集キー/
■7011 / inTopicNo.42)  Re[14]: Control.Invokeが使えない件。
□投稿者/ れい (79回)-(2007/08/28(Tue) 20:13:17)
2007/08/28(Tue) 20:20:19 編集(投稿者)
No6960 (えムナウ さん) に返信
> 今回のコードではForm2を実行しているスレッドでは Me.Text = text を通る前に FormClosingイベントを通っていないことが担保されます。
> いかがでしょうか?

これでも、Me.Text = textを通る前に FormClosingイベントを通っていないことは保証できません。
ダメなんです…。

    Public Sub WriteLine(ByVal text As String)
        Dim lockedFormClose As Boolean
        SyncLock formCloseLockObject
            lockedFormClose = formClose '(a)
        End SyncLock
        If Not lockedFormClose Then
            If Me.InvokeRequired Then
                Me.Invoke(New WriteLineDelegate(AddressOf WriteLine), New Object() {text}) '(b)
            Else
                Me.Text = text
                Me.TextBox1.Text = text
                Me.TextBox1.ScrollToCaret()
                Me.TextBox1.Focus()
            End If
        End If
    End Sub

上記コードで説明します。
まだフォームの終了処理が始まっていない状態で、
Invokeする側のスレッドが(a)でformCloseを取得します。
FormCloseはその時点でまだfalseなので、lockedFormCloseもfalseとなり、
そのままこのスレッドは(b)まで進みます。

しかしこの間に、フォーム側スレッド、Invokeされる側のスレッドは、
Form.Closingを通過し、formCloseを設定してClosedも通過する可能性があります。

そうすると、Invokeする側のスレッドは、廃棄途中か廃棄ごのControlにInvokeしますので、
エラーを吐くかブロックしてフリーズします。

えむナウさんのコードは(a)から(b)まで非常に短いので、
落ちづらいであろうとは思いますが、
安全なコードとはいえません。

実際に試してもみました。
10プロセス同時に起動し、放置したところ、
2個のプロセスが死亡、残り8個のプロセスが20000〜50000回フォームを閉じたところでフリーズしました。

実用上問題ないといえば無いのですが、
どんなコードを組んでも、スレッドセーフなプログラミングができないというのは、
気分が悪くて仕方ありません。

私の知恵では八方塞です。
どなたか、なにかアイデアをお持ちでしたらぜひ教えてください。

もう試したいことがあるので、
それが終わったら閉じます。

(「解決済み」機能を最近知ったので是非使ってみたい!

引用返信 編集キー/
■7012 / inTopicNo.43)  Re[15]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) (320回)-(2007/08/28(Tue) 20:43:50)
渋木宏明(ひどり) さんの Web サイト
> どなたか、なにかアイデアをお持ちでしたらぜひ教えてください。

FormClosing を差し戻すしかないです。

比較的マジメにやるなら、FormClosing に入ったら

・とりあえず this.Hide()
・ワーカースレッドに対して非同期に終了処理を要請して FormClosing をキャンセル
・ワーカースレッドの終了通知により本来の Close() 処理に移行

みたいな流れです。



引用返信 編集キー/
■7014 / inTopicNo.44)  Re[16]: Control.Invokeが使えない件。
□投稿者/ れい (80回)-(2007/08/28(Tue) 21:07:04)
No7012 (渋木宏明(ひどり) さん) に返信
>>どなたか、なにかアイデアをお持ちでしたらぜひ教えてください。
>
> FormClosing を差し戻すしかないです。

やっぱり戻すしかないですか。
他のロジック入ってたりすると大変ですね。

今Nyaruruさんの解析結果を考慮して、
ShowDialog終了後に同期機構を入れてみて試してます。

差し戻しは実装がめんどくさいですが、
時間をみつけてやってみます。

BeginInvokeなら安心かと思っていたんですが、
散々しらべたところBeginInvokeでもたまに死ぬことがわかりました。
フリーズはしないですが。

Invoke、BeginInvokeがうまく呼べない場合、
本来は例外を吐く仕様になっている(ようだ)というのもわかりました。
内部で散々チェックしてます。
引用返信 編集キー/
■7018 / inTopicNo.45)  Re[17]: Control.Invokeが使えない件。
□投稿者/ えムナウ (88回)-(2007/08/28(Tue) 21:59:40)
そうですね。

そこまで細かいことを考えるなら
FormClosingをキャンセルするしかないですね。
いずれにしろ方法はあります。

1000回とかで議論していたように感じていたので
とりあえず、うちで3万回以上動けばいいかなって思ってましたが。

引用返信 編集キー/
■7020 / inTopicNo.46)  Re[18]: Control.Invokeが使えない件。
□投稿者/ れい (83回)-(2007/08/28(Tue) 22:31:26)
No7018 (えムナウ さん) に返信
> そうですね。
>
> そこまで細かいことを考えるなら

やっぱり細かいですか。

正しい・正しくない
バグ有り・バグ無し

の間には大きな差があると思うんですが。
.Netのバグ、ってことなら、私もすっきりなんです。
誰も文句を言わないので私の知恵が足りないのかなと。

問題があったとき、私がいつも知りたいのは、

1 この振舞いが製作者の意図どおりかどうか、
2 製作者の意図では、どうコードを書くべきだったのか、
3 意図通りに振舞っていないなら、どこが問題で、
4 現実にはどう対策すべきなのか、

この4点です。
今回はマルチスレッドだし、Formだし、だったので、
私には1の確証が全然得られなかったのが一番の問題でした。
それなのに

> 1000回とかで議論していたように感じていたので
> とりあえず、うちで3万回以上動けばいいかなって思ってましたが。

確率で論じてしまったのがいけないですね。
1回で確実に再現させられるコードが作れなかったからなんですが、
みなさんには手数・時間をおかけしました。

2
.Netにバグが無かったなら、ロックやフラグは一切無しで動くべきもののようです。

3の問題点は、
.Net2.0に関してはNyaruruさんのおかげでだいぶわかりました。

> FormClosingをキャンセルするしかないですね。
> いずれにしろ方法はあります。

4の対策はぼちぼち調べます。
確率的に低いので、対策なしでもそんなに問題ないですし。
引用返信 編集キー/
■7025 / inTopicNo.47)  Re[19]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) (321回)-(2007/08/29(Wed) 09:10:57)
渋木宏明(ひどり) さんの Web サイト
>>そこまで細かいことを考えるなら
>
> やっぱり細かいですか。

いや、そういう状況(廃棄中のフォームに対して Control.Invoke() するかもしれない状況)があり得て、かつその時 Control.Invoke() がブロックしてしまうことを知っているなら、テストやサンプルならともかく、リリースもので対処しておかないと駄目でしょうね。

前にも書きましたが、僕の場合は別の理由もあって「そういう状況」が起きないようにアプリの作りを調整しています。

基本的に、FormClosing まできている状態でワーカースレッドに対して終了を指示して、それを同期的に待つような構造はあまりよくないんです。

例えば

・通信スレッドがある
・通信スレッドはメインフォームに対してログ表示を Control.Invoke() を依頼する
・通信スレッドは通信停止を命じられた時、「通信終了」をログ表示と同じように Control.Invoke() で依頼する

ような場合

・FormClosing で通信スレッドに対して終了を通知
・そこでそのまま通信スレッドの終了を待機する

ような作りにするとブロックしてしまいます。

この流れでブロックするのは当たり前の話なわけですが、こんな単純なパターンであっても「FormClosing でワーカースレッドの終了を待つ」のは「スレッドが何をしてるのか(この場合は Control.Invoke() していないか)」を考慮しておかないと駄目だということです。

この局面で、同期そのものだけでなくワーカースレッドの振る舞いまで考慮に入れておかなくてはならない、というのは実装パターンとしてはあまりよくないと考えました。
少なくとも汎用的な実装パターンとして受け入れることは出来ません。

といったわけで、「FormClosing に来てから待ちあわせをする」のは避けるようにしてきました。

なので、幸運にもこれまで「Control.Invoke() のバグ」にあたることはありませんでしたが、上記とは別に「Control.Invoke() がバグってる」のが分かってるなら、対処を検討したりMSのサポートに連絡したりといった努力は有益なことと思います。

引用返信 編集キー/
■7028 / inTopicNo.48)  Re[20]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) (322回)-(2007/08/29(Wed) 09:55:07)
渋木宏明(ひどり) さんの Web サイト
> なので、幸運にもこれまで「Control.Invoke() のバグ」にあたることはありませんでしたが、上記とは別に「Control.Invoke() がバグってる」のが分かってるなら、対処を検討したりMSのサポートに連絡したりといった努力は有益なことと思います。

「止まる」サンプルもあるんだし、サポートに投げちゃった方が早くないですか?

hotfix が出るかどうかは疑問だけど、Control.Invoke() の実装のバグならインシデントは返って来るだろうし。

引用返信 編集キー/
■7077 / inTopicNo.49)  Re[21]: Control.Invokeが使えない件。
□投稿者/ れい (86回)-(2007/08/29(Wed) 22:36:09)
No7028 (渋木宏明(ひどり) さん) に返信
>>なので、幸運にもこれまで「Control.Invoke() のバグ」にあたることはありませんでしたが、上記とは別に「Control.Invoke() がバグってる」のが分かってるなら、対処を検討したりMSのサポートに連絡したりといった努力は有益なことと思います。
>
> 「止まる」サンプルもあるんだし、サポートに投げちゃった方が早くないですか?
>
> hotfix が出るかどうかは疑問だけど、Control.Invoke() の実装のバグならインシデントは返って来るだろうし。

そうですねぇ。

いろんなものが自分の管理下にあるなら問い合わせてもいいんですが、
権利関係が微妙なんですよね。
自分の立場を守るためには放置せざるをえないですね。

そういった努力のできる方が現れるのを期待して、じっと待つことにします。

引用返信 編集キー/
■7078 / inTopicNo.50)  Re[22]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) (324回)-(2007/08/29(Wed) 23:02:07)
渋木宏明(ひどり) さんの Web サイト
> いろんなものが自分の管理下にあるなら問い合わせてもいいんですが、
> 権利関係が微妙なんですよね。
> 自分の立場を守るためには放置せざるをえないですね。

なら、「情報として」でも MSDN Wiki や MSDN Forum の方に書いておくとか。

引用返信 編集キー/
■7079 / inTopicNo.51)  Re[23]: Control.Invokeが使えない件。
□投稿者/ れい (87回)-(2007/08/29(Wed) 23:02:46)
No7078 (渋木宏明(ひどり) さん) に返信
>>いろんなものが自分の管理下にあるなら問い合わせてもいいんですが、
>>権利関係が微妙なんですよね。
>>自分の立場を守るためには放置せざるをえないですね。
>
> なら、「情報として」でも MSDN Wiki や MSDN Forum の方に書いておくとか。
>

了解しました。
引用返信 編集キー/
■7139 / inTopicNo.52)  Re[17]: Control.Invokeが使えない件。
□投稿者/ れい (92回)-(2007/08/30(Thu) 16:32:02)
No7014 (れい)
> 今Nyaruruさんの解析結果を考慮して、
> ShowDialog終了後に同期機構を入れてみて試してます。
>
> 差し戻しは実装がめんどくさいですが、
> 時間をみつけてやってみます。
>
> BeginInvokeなら安心かと思っていたんですが、
> 散々しらべたところBeginInvokeでもたまに死ぬことがわかりました。
> フリーズはしないですが。

えーと。
回しまくって調べてみました。
そろそろかわいそうなのと、バレるとやばいので、もうやめることにします。

・Invokeなし(Control)
 PostMessageとInterlockedで親と通信。画面描画などは同程度に行う。
1,000,000を超えましたが、動いてます。

・BeginInvoke
 InvokeでなくBeginInvokeだけ。
フリーズはありません。
Invoke先スレッドが再現性ない例外を吐きます。
300回程度回ります。

・ShowDialog後に同期機構を入れる
 Invokeが終わるまでスレッドが死なないようにした。
フリーズします。
Invoke先スレッドは再現性ない例外をを吐きます。
10,000ループ程度でした。

・Closingで一回戻る。
 Closingで戻ってInvokeを禁止し、全てのInvokeが終了するまで待ち、それから再度Closeしてみました。
Invoke先スレッドが2回ほど例外を投げて異常終了しました。原因は不明です。
フリーズはありません。
100,000回程度は回りました。

※怖いのでハードウェアのエラー検査も行いましたが、問題は見つかりませんでした。
※停止頻度が低いので、全て数回しか試していません。つまり、数字は統計学的に有意ではありません
※テストはすべて2xXeonで、4Threadな環境です。
※.Net1.1、.Net2.0に違いはありませんでした。

テスト結果は以上です。

考察

残念ながら、「BeginInvoke」も「ShowDialog終えてから待つ」も「Closing差し戻し」もダメでした。

Invoke先スレッドがごく稀に例外吐きます。
PostMessageやWM_TIMER、同期オブジェクトを組み合わせて、自分で実装すると問題なく動きます。

BeginInvokeでもInvokeと同様にInvoke先が例外を吐いて異常終了します。
いろいろな掲示板にInvokeではなくBeginInvokeを使えと書いてありますが、
Invoke先が死ぬ確率は同程度で、停止を回避できるだけです。

いつ閉じられるかわからないFormにInvokeすると、
停止や異常終了を引き起こす場合があるのは回避不可能でした。

結論

フォームを自動で開いたり閉じたりするプログラムで、
且つInvoke・BeginInvokeで非同期通信すると、
10000回ぐらいで停止したり死んだりするので、
よくないよ。

それ以前にそんなのはUIとして大失敗ですよ。
解決済み
引用返信 編集キー/

<前の20件
トピック内ページ移動 / << 0 | 1 | 2 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -