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

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

ログ内検索
  • キーワードを複数指定する場合は 半角スペース で区切ってください。
  • 検索条件は、(AND)=[A かつ B] (OR)=[A または B] となっています。
  • [返信]をクリックすると返信ページへ移動します。
キーワード/ 検索条件 /
検索範囲/ 強調表示/ ON (自動リンクOFF)
結果表示件数/ 記事No検索/ ON
大文字と小文字を区別する

No.6843 の関連記事表示

<< 0 | 1 | 2 >>
■6843  Control.Invokeが使えない件。
□投稿者/ れい -(2007/08/24(Fri) 19:16:28)

    分類:[.NET 全般] 

    こちら(http://bbs.wankuma.com/index.cgi?mode=al2&namber=6760)で話してたんですが、
    皆さんの意見を聞きたいので、タイトルと内容を合わせてスレッド立てます。

    タイトルの通り、「Control.Invokeが使えない」件に関して、
    私の事実誤認であるとか、同意であるとか、皆さんの意見をください。

    Control.InvokeやEndInvokeを何も考えずに使うと、
    Controlを廃棄した際にデッドロックします。
    Invokeがウィンドウメッセージを送り、返事を待ってる間に
    Controlが廃棄された場合、いつまで待っても返事が返ってこないからです。
    これは
    http://osdir.com/ml/windows.devel.dotnet.clr/2004-04/msg00157.html
    http://www.microsoft.com/japan/msdn/community/gdn/ShowPost-26227.htm
    などなど、かなり既出の件です。

    この対策をいろいろ考えたのですが、
    Control.Invokeを安全に使える方法が全く存在せず、
    Invokeを使うこと自体が間違いであるという結論に至ってしまいました。

    ControlやFormはいつ廃棄されるのか、
    Invokeする側(Invoker)からはわかりません。
    Invokeされる側(Invokee)でフラグを立てて回避しようとしても、
    Invokeメソッド内で実際にウィンドウメッセージを送るまでの間に
    ControlやFormが死んでしまうとデッドロックします。

    WorkerThreadをInvokerとする場合も同様で、
    WorkerThreadが死ぬ前にControlが死ぬ可能性のある場合は、
    WorkerThreadがInvokeでデッドロックし、残ってしまいます。
    Controlは、WM_DESTROYやWM_QUITで、いつでも死ぬ可能性があり、
    Controlが死ぬ前にWorkerThreadが死ぬのを保証するには、Abortし終了を待つしかありませんが、
    その間にInvokeされてる場合もありうるのでダメです。

    いろいろ考えましたが、どうやってもControl.Invokeを安全には使えませんでした。
    代替方法はBeginInvokeを投げっぱなしにするか、EndInvokeをタイムアウト付きで呼ぶことでした。

    ですが、
    http://msdn2.microsoft.com/ja-jp/library/757y83z4(VS.80).aspx
    http://www.atmarkit.co.jp/fdotnet/dotnettips/312ctrlinvoke/ctrlinvoke.html
    http://codezine.jp/a/article.aspx?aid=139#invoke
    http://dobon.net/vb/dotnet/programing/progressdialog.html
    などなど、
    ネット上にも、MSDNにもInvokeを使ったサンプルはたくさんあります。
    見る限りどれもデッドロックを引き起こすコードで、
    いくつかは検証してみましたが、実際にデッドロックを引き起こしました。
    MSDNのサンプルでさえ、Formを閉じたときにスレッドがWaitのまま残りました。

    私が検証した限りでは、安全に使うことはできませんでしたし、
    理論上できないのではないかと私は思っています。
    もし安全に呼ぶ方法が一つでもあるなら存在意義がありますが、
    一つも無いなら、ただ邪魔なだけのメソッドです。

    だれか発言力のある偉い人が、「Control.Invokeはダメ」って言ってくれれば安心するんですが、
    見当たりません。
    私一人が思ってるだけ、言ってるだけでは、自分自身も説得できなくて。
    誰か偉い人、「ダメ」って言ってくれませんか?

    また、「きちんと使えてる。お前のチエが猫並みなだけ」って人がいるなら、
    是非猫にチエをご教授ください。
親記事 /過去ログ18より / 関連記事表示
削除チェック/

■6846  Re[1]: Control.Invokeが使えない件。
□投稿者/ NyaRuRu -(2007/08/24(Fri) 23:10:15)
    2007/08/24(Fri) 23:14:17 編集(投稿者)

    No6843 (れい さん) に返信
    > Control.InvokeやEndInvokeを何も考えずに使うと、
    > Controlを廃棄した際にデッドロックします。
    > Invokeがウィンドウメッセージを送り、返事を待ってる間に
    > Controlが廃棄された場合、いつまで待っても返事が返ってこないからです。

    ほんとかなと思って試してみましたが,
    以下のケースでは閉じたフォームに対する Invoke で InvalidOperationException 例外が発生しました.
    以下 C# ですが,Form を作って Load イベントを次のように設定してください.

    private void Form1_Load(object sender, EventArgs e)
    {
    Button button = new Button();
    button.Click +=
    delegate(object _sender, EventArgs _e)
    {
    Form frm = new Form();
    frm.Show();
    Thread thread = new Thread(delegate()
    {
    try
    {
    while (true)
    {
    frm.Invoke(
    (MethodInvoker)
    delegate { frm.Text = DateTime.Now.ToString(); });
    Thread.Sleep(1000);
    }
    }
    catch (Exception exp)
    {
    MessageBox.Show(exp.Message);
    }
    });
    thread.Start();
    };
    this.Controls.Add(button);
    }
    ボタンを押すとスレッドが起動され,
    表示用フォームのタイトルを現在時刻で更新していきます.

    Windows のメッセージキューはスレッドごとに独立しており,
    ひとつのメッセージキューに複数のウィンドウが所属します.
    これを称して,「ウィンドウ1はスレッドXに所有される」みたいな感じになると.
    ・スレッドX
    メッセージポンプ
    ウィンドウ1
    ウィンドウ2
    ウィンドウ3
    が,http://bbs.wankuma.com/index.cgi?mode=al2&namber=6760 では
    別スレッドでウィンドウを開いていますよね? あれって
    ・スレッドX
    メッセージポンプ
    ウィンドウ1
    ・スレッドY
    メッセージポンプ
    ウィンドウ2
    みたいなことをしたいということなのでしょうか?
    (と,れいさんに聞いても仕方がないと思いますが)
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6847  Re[2]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) -(2007/08/24(Fri) 23:46:58)
>
    > ほんとかなと思って試してみましたが,
    > 以下のケースでは閉じたフォームに対する Invoke で InvalidOperationException 例外が発生しました.

    やっぱりそうですよねぇ。

    Control.Invoke() が最終的に SendMessage() を呼び出しているなら、存在しないウィンドウへの SendMessage() はブロックではなくエラーになるはずで、その時例外が発生するというのは(とりあえずは)納得できる実装です。

    Control.Invoke() で立ち往生するケースがあるということですが、SDK レベルで SendMessage() を使っていても、使い方がまずければデッドロックします。

    なので、Control.Invoke() でデッドロックするのも似たような状況なんじゃないかなーと。
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6849  Re[2]: Control.Invokeが使えない件。
□投稿者/ れい -(2007/08/25(Sat) 02:34:12)
    2007/08/25(Sat) 02:48:14 編集(投稿者)
    No6846 (NyaRuRu さん) に返信
    > 2007/08/24(Fri) 23:14:17 編集(投稿者)
    > ■No6843 (れい さん) に返信
    >>Control.InvokeやEndInvokeを何も考えずに使うと、
    >>Controlを廃棄した際にデッドロックします。
    >
    > ほんとかなと思って試してみましたが,
    > 以下のケースでは閉じたフォームに対する Invoke で InvalidOperationException 例外が発生しました.

    スレッド間通信の問題ですので、一回だけでは再現しない場合もあります。
    連続で何回も行ったり、高負荷時に試さなくてはいけません。
    ダメな同期機構だと、開発時には大丈夫なのに、ある環境ではいつもダメ、
    見たいなことがありますので、たくさん試して1回でもダメならダメです。
    問題ないことの証明は激しく難しいですが。

    適当な時間経ったら自動で閉じるFormをつくり、
    延々とFormを作ってはInvoke、Formを作ってはInvoke、を繰り返して確認したところ…
    Invokeで発生する可能性のある例外は、
    ・InvalidOperationException
    ・ObjectDisposedException
    ・System.ComponentModel.InvalidAsynchronousStateException(2.0のみ
    でした。
    また、600〜3000回に1回ほどInvokeでフリーズしました。
    Invoke先で何もしないと3000回試してもフリーズしませんでした。
    (最初は2回に1回くらいだったんですが、再起動したら減りました。)
    環境とかで問題が発生する確率が大きく変わるので頻度は参考程度ですが、
    http://bbs.wankuma.com/index.cgi?mode=al2&namber=6760 のコードでは
    デッドロックが起こりえることは間違いありません。

    > 別スレッドでウィンドウを開いていますよね? あれって
    >略
    > みたいなことをしたいということなのでしょうか?

    あのスレッドではそうでしょう。
    あんまりないですが、こういう仕様にしたいときもたまにあります。
    Control.InvokeはGUIが無くても止まりますので、今回はあまり関係ないです。

    No6847 (渋木宏明(ひどり) さん) に返信
    > Control.Invoke() が最終的に SendMessage() を呼び出しているなら、存在しないウィンドウへの SendMessage() はブロックではなくエラーになるはずで、その時例外が発生するというのは(とりあえずは)納得できる実装です。

    はい。おかしいときに例外は正しい。私もそれを期待します。
    デッドロックは困る。

    ちなみに、Control.InvokeはPostMessageを使っています。

    > Control.Invoke() で立ち往生するケースがあるということですが、SDK レベルで SendMessage() を使っていても、使い方がまずければデッドロックします。
    > なので、Control.Invoke() でデッドロックするのも似たような状況なんじゃないかなーと。

    変な使い方をすればうまく動かないってのはなんでもそうなので、いいと思います。
    Control.Invoke()でデッドロックしないコードが見当たらないのが
    問題ではないかと。

    Invoke先で何もしなければ今のところデッドロックは起こしませんが、
    何もしなければInvokeの意味がないですし、
    本当にデッドロックしないかもわかりません。
    もしかしたら条件によってはロックするかも知れません。

    どういうInvoke内容だったら安全なんでしょうか?
    MSDNをいくら読んでも、どこを探しても、明確な指針が見当たりません。
    それで皆さんの意見を聞きたかったのです。

    話の内容からすると、
    Nyaruruさんも渋木宏明(ひどり)さんも、
    Control.Invokeのデッドロックに遭遇したことがないようですね。

    以前問題を見つけたときはほぼ毎回デッドロックしてたんですが、
    今調べてみると、たまにしか止まらなくって検証が大変です。
    はてさて、どうしたものか。
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6851  Re[3]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) -(2007/08/25(Sat) 03:05:09)
>
    > スレッド間通信の問題ですので、一回だけでは再現しない場合もあります。

    精密に内容を把握していなかったかも>自分

    「Control.Invoke() の使用による不都合の原因は Windows のメッセージング機構に問題がある」という話だと思ってたんですが、そうではなくて「不都合の原因は Control.Invoke() の実装にあるのでは?」というお話なんですね?

    とすると、ヘンテコな現象が起きる原因は

    > ちなみに、Control.InvokeはPostMessageを使っています。

    な気がします。

    同期メッセージング用に用意されている SendMessage() を使わないで、非同期メッセージングを使って外側で、自前で同期を取ろうとしているところに無理が出てるのかもしれません。

    > どういうInvoke内容だったら安全なんでしょうか?

    ウィンドウやコモンコントロールのラッパに関して言えば、基本的には Control.Invoke() から戻ってくるまでの間、Control.Invoke() 先が有効なウィンドウハンドルを保持していればよろしんじゃないでしょうか。

    もちろん、SendMessage() ですらデッドロックを起こすような変なことをしていないのが前提です。

    > 話の内容からすると、
    > Nyaruruさんも渋木宏明(ひどり)さんも、
    > Control.Invokeのデッドロックに遭遇したことがないようですね。

    無いです。

    先にも述べたとおり、元々 SDK レベルで SendMessage() を使っている場合でさえ、条件が悪ければデッドロックを起こすことは分かっていますが、きわどいタイミングでメッセージ伝達が起きないように VC++ を多用していた頃からオブジェクトの寿命管理を徹底しています。
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6860  Re[4]: Control.Invokeが使えない件。
□投稿者/ れい -(2007/08/25(Sat) 05:11:02)
    No6851 (渋木宏明(ひどり) さん) に返信
    >>どういうInvoke内容だったら安全なんでしょうか?
    >
    > ウィンドウやコモンコントロールのラッパに関して言えば、基本的には Control.Invoke() から戻ってくるまでの間、Control.Invoke() 先が有効なウィンドウハンドルを保持していればよろしんじゃないでしょうか。

    普通そう思いますよね。
    ですが、Invokeから戻るまでの間、Windowを保持するのが、結構難しい。
    Invokeされてる場合はすぐに終了せずに、ClosingでCancelを返して…
    ってなことを、別の同期機構を入れて処理しなきゃならんのです。
    できなくはないと思うんですが、
    メッセージループ部も、Controlの終了処理部分も隠蔽されてるので、
    自分で違う通信方法を作ったほうがよっぽど早くて確実って話になってしまいます。

    私も自分で実装してしまいましたし、
    渋木宏明(ひどり)さんやNyaruruさんも自分でやってしまうとは思うんですが。
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6850  Re[3]: Control.Invokeが使えない件。
□投稿者/ れい -(2007/08/25(Sat) 02:58:23)
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6852  Re[3]: Control.Invokeが使えない件。
□投稿者/ NyaRuRu -(2007/08/25(Sat) 03:38:05)
    No6849 (れい さん) に返信
    > どういうInvoke内容だったら安全なんでしょうか?
    > MSDNをいくら読んでも、どこを探しても、明確な指針が見当たりません。
    > それで皆さんの意見を聞きたかったのです。

    どんな範囲なら安全かどうかは必要になってから自分で調べます.
    もちろん Win32 レベルで調べます.多分 .NET の層だけでは完結しないでしょう.
    .NET の層だけで明確な指針を作りたいとは思いません.

    > 話の内容からすると、
    > Nyaruruさんも渋木宏明(ひどり)さんも、
    > Control.Invokeのデッドロックに遭遇したことがないようですね。

    > 以前問題を見つけたときはほぼ毎回デッドロックしてたんですが、
    > 今調べてみると、たまにしか止まらなくって検証が大変です。
    > はてさて、どうしたものか。

    私の場合はあんまり GUI アプリケーションを作らないですからねぇ.
    まあ目の前で起きれば調べますが.

    あと,単にメッセージポンプが止まっただけのような状況を私はデッドロックとは呼んでこなかったのですが,今回の件をデッドロックと呼ぶのは混乱の元だったりしませんか?
    (実際そのハング時に何が起きているのかちゃんと調べたわけじゃないですが)
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6855  Re[4]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) -(2007/08/25(Sat) 04:09:21)
>
    > あと,単にメッセージポンプが止まっただけのような状況を私はデッドロックとは呼んでこなかったのですが,今回の件をデッドロックと呼ぶのは混乱の元だったりしませんか?

    例えば、ワーカスレッドでウィンドウを作ったとして、同じスレッドでメッセージポンプを回さないのはまずいすね。

    Control.Invoke() の実装が SendMessage() ではなく PostMessage() を使用しているなら、メッセージ送信先でメッセージポンプが回っていなければメッセージキューが詰まった時点でブロックかな?

    SendMessage() を使っていれば同期的に WndProc() が直接呼び出されるので、そういう「ふん詰まり」は原則おきませんが。
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6854  Re[3]: Control.Invokeが使えない件。
□投稿者/ NyaRuRu -(2007/08/25(Sat) 03:52:24)
    No6849 (れい さん) に返信
    >>別スレッドでウィンドウを開いていますよね? あれって
    > >略
    >>みたいなことをしたいということなのでしょうか?
    >
    > あのスレッドではそうでしょう。
    > あんまりないですが、こういう仕様にしたいときもたまにあります。
    > Control.InvokeはGUIが無くても止まりますので、今回はあまり関係ないです。

    うーん,本当に関係ないんですかねぇ?
    私がいいたいのは GUI あるなしじゃなくて,メインスレッドにおける Application.Run(new Form1()); がワーカースレッドにないことの影響とか,ちゃんと考えました? ということなのですが.
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6856  Re[3]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) -(2007/08/25(Sat) 04:11:31)
>
    > Control.InvokeはGUIが無くても止まりますので、今回はあまり関係ないです。

    GUI の無い Control.Invoke() ってどゆこと???

    Control クラスは Windows.Forms 所属のクラスなので、Control 派生クラスを使って時点で GUI はそこにあると思うんですが。
    (ユーザに見えるかどうかは別として)
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6858  Re[4]: Control.Invokeが使えない件。
□投稿者/ れい -(2007/08/25(Sat) 04:48:28)
    No6851 (渋木宏明(ひどり) さん) に返信
    > 「Control.Invoke() の使用による不都合の原因は Windows のメッセージング機構に問題がある」という話だと思ってたんですが、そうではなくて「不都合の原因は Control.Invoke() の実装にあるのでは?」というお話なんですね?

    そうです。
    内部でAsyncWaitHandleをずっと待ち続けてるようなので、

    No6852 (NyaRuRu さん) に返信
    > あと,単にメッセージポンプが止まっただけのような状況を私はデッドロックとは呼んでこなかったのですが,今回の件をデッドロックと呼ぶのは混乱の元だったりしませんか?

    ポンプが止まってるだけではないですが、
    おっしゃるとおり、デッドロックではないですね。
    ロック持ち合ってるわけじゃないですから。
    ハングですね。

    No6856 (渋木宏明(ひどり) さん) に返信
    >>Control.InvokeはGUIが無くても止まりますので、今回はあまり関係ないです。
    > GUI の無い Control.Invoke() ってどゆこと???

    あっと、失礼。言葉が足りない。
    Invokeされる側でなく、Invokeする側の話です。
    Invokeする側はControlを持たないWorkerThreadでも、ハングします。

    No6854 (NyaRuRu さん) に返信
    >>Control.InvokeはGUIが無くても止まりますので、今回はあまり関係ないです。
    > うーん,本当に関係ないんですかねぇ?
    > 私がいいたいのは GUI あるなしじゃなくて,メインスレッドにおける Application.Run(new Form1()); がワーカースレッドにないことの影響とか,ちゃんと考えました? ということなのですが.

    Appication.Runで何か特殊なことをやってるかもしれませんが…。
    メインスレッドからサブスレッドのFormにInvokeしても、
    サブスレッドからメインスレッドのFormにInvokeしても同じでしたから、
    まぁあんまり関係ないかと思ってるんですが。
    Application.Runが影響してるんだとするとかなり醜い実装ですねぇ。

    No6851 (渋木宏明(ひどり) さん) に返信
    > きわどいタイミングでメッセージ伝達が起きないように VC++ を多用していた頃からオブジェクトの寿命管理を徹底しています。

    私も結局自分で同期機構を入れてスレッド間通信してしまってるんですが、
    せっかくあるのに、使いようがないというのが何とも気になります。

    No6855 (渋木宏明(ひどり) さん) に返信
    >>あと,単にメッセージポンプが止まっただけのような状況を私はデッドロックとは呼んでこなかったのですが,今回の件をデッドロックと呼ぶのは混乱の元だったりしませんか?
    > 例えば、ワーカスレッドでウィンドウを作ったとして、同じスレッドでメッセージポンプを回さないのはまずいすね。
    > Control.Invoke() の実装が SendMessage() ではなく PostMessage() を使用しているなら、メッセージ送信先でメッセージポンプが回っていなければメッセージキューが詰まった時点でブロックかな?
    > SendMessage() を使っていれば同期的に WndProc() が直接呼び出されるので、そういう「ふん詰まり」は原則おきませんが。

    件のコードではShowDialogで回してます。
    Control.Invokeの実装は間違いなくPostMessageです。
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6861  Re[5]: Control.Invokeが使えない件。
□投稿者/ NyaRuRu -(2007/08/25(Sat) 05:12:56)
    2007/08/25(Sat) 05:24:22 編集(投稿者)

    No6858 (れい さん) に返信
    > Appication.Runで何か特殊なことをやってるかもしれませんが…。
    > メインスレッドからサブスレッドのFormにInvokeしても、
    > サブスレッドからメインスレッドのFormにInvokeしても同じでしたから、
    > まぁあんまり関係ないかと思ってるんですが。
    > Application.Runが影響してるんだとするとかなり醜い実装ですねぇ。

    いや,PostMessage ってことならなおさら Win32 のメッセージ通信なわけで,DispatchMessage がいつどこでどのように起きるかは意識しないとまずいでしょう.
    その辺をすっとばして,コントロールの寿命のみに注目して再現条件を考えて良いのかどうか,私にはまだよく分かりません.(まあこれは私が GUI アプリをそんなに作らないからですが)
    .NET のプログラミングモデル上は「メインスレッドからサブスレッドのFormにInvoke」しているように見えても,実際のメッセージキューはスレッドごとにしか存在せず,従ってこれはフォーム間通信ではなくてスレッド間通信だと考えるのが妥当だと思います.

    なお,WPF の資料ですが,
    http://msdn2.microsoft.com/ja-jp/library/ms741870(vs.80).aspx#stumbling_points
    の「再入およびロック」のところでも解説されているように,GUI アプリケーションではかなり広範囲に Win32 メッセージの影響を考えないといけないので,出発点からして「綺麗」とはほど遠い状況です.
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6862  Re[6]: Control.Invokeが使えない件。
□投稿者/ れい -(2007/08/25(Sat) 06:52:47)
    2007/08/25(Sat) 06:54:27 編集(投稿者)

    No6861 (NyaRuRu さん) に返信
    >>まぁあんまり関係ないかと思ってるんですが。
    > いや,PostMessage ってことならなおさら Win32 のメッセージ通信なわけで,DispatchMessage がいつどこでどのように起きるかは意識しないとまずいでしょう.
    >その辺をすっとばして,コントロールの寿命のみに注目して再現条件を考えて良いのかどうか,私にはまだよく分かりません.(まあこれは私が GUI アプリをそんなに作らないからですが)
    >.NET のプログラミングモデル上は「メインスレッドからサブスレッドのFormにInvoke」しているように見えても,実際のメッセージキューはスレッドごとにしか存在せず,従ってこれはフォーム間通信ではなくてスレッド間通信だと考えるのが妥当だと思います.

    んーと、話が通じてないですかね。
    メインスレッドはApplication.Run経由でどこかにあるメッセージループを回していて、
    サブスレッドはShowDialog経由でどこかにあるメッセージループを回していますね。

    例のコードの場合、

    メインスレッドでInvokeすると、サブスレッドのキューに該当するHWNDをセットしたInvoke用メッセージを投げます。
    メッセージ投げたら、WaitHandleで待ちます。
    サブスレッドはShowDialogの中でキューからInvoke用メッセージを拾い、
    HWNDに該当する窓にDispatchします。
    窓のWndProc内でInvoke用のメッセージだと認識して、
    メッセージのパラメータとかからデリゲートを作り、呼びます。
    デリゲートの実行が終わったら、WaitHandleをSetして、
    またメッセージループに戻ります。
    で、何回もやると何処かでWaitHandleのSetを忘れてフリーズしちゃうわけです。

    これが
    > メインスレッドからサブスレッドのFormにInvokeしても、
    の場合で、

    逆の場合、
    > サブスレッドからメインスレッドのFormにInvokeしても同じでしたから、
    も同様で、
    サブスレッド側ではShowDialogで回ってて、そこからメインスレッドの管理下にある窓にPostMessage、
    メインスレッドはApplication.Run内でメッセージ拾って窓にDispatchして、実行してWaitHandle.Set。
    これも何回もやるとフリーズ。

    ちなみに、サブスレッドからサブスレッドの窓へInvokeしても、
    同様にフリーズします。

    DispatchMessageがある場所とPostMessageでのメッセージの流れを書くと、
    ・Application.Runの中 -> ShowDialogの中
    ・ShowDialogの中 -> Application.Runの中
    ・ShowDialogの中 -> ShowDialogの中
    となります。

    この3者で、同じようにフリーズしてるなら、問題は
    ・Controlの廃棄
    ・メッセージループの停止
    辺りであろうと判断しても妥当だと思いますが、いかが?

    というか、

    > いや,PostMessage ってことならなおさら Win32 のメッセージ通信なわけで,DispatchMessage がいつどこでどのように起きるかは意識しないとまずいでしょう.
    >その辺をすっとばして,コントロールの寿命のみに注目して再現条件を考えて良いのかどうか,私にはまだよく分かりません.(まあこれは私が GUI アプリをそんなに作らないからですが)

    すっ飛ばして考えていたわけではなく、
    上記のように意識していましたし、そのつもりで書いてたんですが、
    伝わらなかったのでしょうか?
    それとも、上記考慮では足りないですか?
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6865  Re[7]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) -(2007/08/25(Sat) 10:04:27)
>
    うーん、結局原因がどこにあると主張(あるいは推察)しているのかよく分からないです。

    > メインスレッドでInvokeすると、サブスレッドのキューに該当するHWNDをセットしたInvoke用メッセージを投げます。
    > メッセージ投げたら、WaitHandleで待ちます。
    > サブスレッドはShowDialogの中でキューからInvoke用メッセージを拾い、
    > HWNDに該当する窓にDispatchします。
    > 窓のWndProc内でInvoke用のメッセージだと認識して、
    > メッセージのパラメータとかからデリゲートを作り、呼びます。
    > デリゲートの実行が終わったら、WaitHandleをSetして、
    > またメッセージループに戻ります。
    > で、何回もやると何処かでWaitHandleのSetを忘れてフリーズしちゃうわけです。

    とのことですが、どこにも「フォームを閉じる」「ダイアログを閉じる」って書いてないところをみると、ここでは Invoke() の投げ先のフォームなりコントロールは常態で生存しているんですよね?

    であるならば、

    > この3者で、同じようにフリーズしてるなら、問題は
    > ・Controlの廃棄
    > ・メッセージループの停止
    > 辺りであろうと判断しても妥当だと思いますが、いかが?

    と言われている「Controlの廃棄」は起きるはずも無く、「メッセージループの停止」も「ヘンなこと」をしていない限り起きないと思うんですけど?

    結局、Control.Invoke() を使うことによる不都合は↓のどちらのパターンであるって話なんでしょう?

    ・Control.Invoke() の投げ先が正常な状態にあっても常に起き得る。
    ・Control.Invoke() の投げ先であるフォーム/コントロールが廃棄途上にある時に起きる。
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6867  Re[8]: Control.Invokeが使えない件。
□投稿者/ れい -(2007/08/25(Sat) 10:32:05)
    2007/08/25(Sat) 10:38:40 編集(投稿者)

    あ、あれ?
    はじめっから全然伝わってない…。

    > 結局、Control.Invoke() を使うことによる不都合は↓のどちらのパターンであるって話なんでしょう?
    > ・Control.Invoke() の投げ先が正常な状態にあっても常に起き得る。
    > ・Control.Invoke() の投げ先であるフォーム/コントロールが廃棄途上にある時に起きる。

    後者です。

    私、日本語が不自由っぽいですね。
    うまく伝えられなくてすみません。

    廃棄中のControlに他のスレッドからInvokeすると止まることがあります。
    Invoke先は他スレッドなので、Invoke先Controlが廃棄中であるかどうか、
    Invoke元スレッドから正確に判断することができません。
    なので、Control.Invokeを安全に使えるケースが存在しない、と言ってます。

    伝わったでしょうか?
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6869  Re[9]: Control.Invokeが使えない件。
□投稿者/ NyaRuRu -(2007/08/25(Sat) 12:01:05)
    2007/08/25(Sat) 12:27:44 編集(投稿者)
    2007/08/25(Sat) 12:07:58 編集(投稿者)

    No6867 (れい さん) に返信
    > なので、Control.Invokeを安全に使えるケースが存在しない、と言ってます。
    >
    > 伝わったでしょうか?

    れいさんは「Control.Invoke は使えない」と主張されたいようですが,私も「Control.Invoke を使って問題になることがある」という点は,今までの話を見た限り,そうなんだろうなぁと思います.
    私がそこを「はいそーですね」と書かないかられいさんが困っているなら書いておきますよ.はい.
    ただそこは私にとって別にどうでもいいというか,よくある落とし穴のひとつみたいなもので,何が起きているのかの詳細の方がどちらかというと興味があるのです.だって Invoke の問題は将来こっそり Fix されるかもしれませんが,根本的な原因が何であったかは将来においても通用する重要な「失敗事例」なわけですし.そこを「まあ多分〜ですよね」で調べずにおいておくのがどうも嫌なだけです.
    たとえば確率的に失敗するという意味では,using 構文も lock 構文も失敗で,この教訓は将来似たような実装をするときに自分でも生かしますよ.
    http://d.hatena.ne.jp/NyaRuRu/20060605

    > 廃棄中のControlに他のスレッドからInvokeすると止まることがあります。
    > Invoke先は他スレッドなので、Invoke先Controlが廃棄中であるかどうか、
    > Invoke元スレッドから正確に判断することができません。

    デバッグに関するポリシーの違いかと.

    私は,「廃棄中のControlに他のスレッドからInvokeすると止まることがある」という事例だけから,「廃棄中のControlに他のスレッドからInvokeしたのが原因」とは判断しないだけです.
    あるいは,「廃棄中のControlに他のスレッドからInvokeしたのが原因」では,私の基準ではバグの原因としての粒度が大きすぎで,「問題があるのは確かだが原因が確定したわけではない」が妥当だと感じるわけですよ.
    なのでそこを突っ込ませていただいているだけです.
    れいさんは十分に調査されていて,その上で判断されているのかもしれませんが,その調べられている途中の情報が抜けている気がします.

    >で、何回もやると何処かでWaitHandleのSetを忘れてフリーズしちゃうわけです。

    特にこの辺.デバッガで待機ハンドルのステートを調べるぐらいはできると思うのですが,その辺をチェックされたということですよね?
    RichEdit の例なんかは,GC スレッドからの Invoke まで絡んでいるように見えましたし.
    私が興味があるのは,それが「何処か」ということの方です.

    まとめ
    (A)「いくつか Control.Invoke が原因でハングすること事例を見つけたので,信頼性の面から使いたくない」に対する回答「そうですね」
    (B)「問題は (1) Controlの廃棄 (2)メッセージループの停止 辺りであろうと判断しても妥当だと思いますが、いかが?」に対する回答「それは推論では? もっと細かく何が起きているのか確かめられそう」

    という感じです.れいさんが (A) にしか興味がなくて,Control.Invoke を封印すればそれでいいということであれば,私から申し上げることは特にないです.
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6868  Re[9]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) -(2007/08/25(Sat) 11:54:09)
>
    > 廃棄中のControlに他のスレッドからInvokeすると止まることがあります。
    > Invoke先は他スレッドなので、Invoke先Controlが廃棄中であるかどうか、
    > Invoke元スレッドから正確に判断することができません。
    > なので、Control.Invokeを安全に使えるケースが存在しない、と言ってます。
    >
    > 伝わったでしょうか?

    伝わりました ;-)

    でも、「そりゃしょーがないじゃん」と思います (^^;

    以前の投稿でも書いていますが、廃棄途上の相手に Control.Invoke() しなければならないような状況を作り出さないように注意しているし、なるべく避けるように仕様と設計を調整しています。

    「Control.Invoke() だから」そう感じるのかもしれませんが、「廃棄中のオブジェクトのメソッド呼び出し」なんか .NET でははなから保証されてません。

    「オブジェクトの実装」として ObjectDisposeException() やらを投げてよこすことはありますが、所詮はランタイムの管理範囲外であるオブジェクトの振る舞いなので、何が起きるかはそのオブジェクトの実装次第です。

    # 「ブロックしないでせめて例外をあげてくれ」という話なら賛同します。

    Control.Invoke() が「同期」しているのはプライマリスレッド/ワーカスレッドの実行コンテキストであって、Control.Invoke() 先のオブジェクトの状態まで保証するもんではないと思います。

    > やっぱり、Control.Invokeは私には使えそうにないです…。
    > 1/500で止まるのは見過ごせないし、
    > どう同期組んでいいのかわからなすぎる。

    常態で十分に機能しているんで、特に問題を感じません。

    フォームやコントロールの廃棄中に Control.Invoke() を許してしまうような設計というか構造がまずいと思うな。

    通信スレッドから Control.Invoke() でプライマリスレッドの管理するウィンドウにログを表示するようなアプリを24時間以上稼動させたこともありますが、Control.Invoke() でフリーズとかデッドロックってのには出くわしたことが無いです。
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6885  Re[10]: Control.Invokeが使えない件。
□投稿者/ れい -(2007/08/26(Sun) 17:49:26)
    No6868 (渋木宏明(ひどり) さん) に返信
    >>伝わったでしょうか?
    > 伝わりました ;-)

    あ。やっと伝わりました。
    で、ようやく次にいけます。

    > フォームやコントロールの廃棄中に Control.Invoke() を許してしまうような設計というか構造がまずいと思うな。

    これなんですが。
    私もそう思い、いろいろ考えたんですが、
    Invoke先がFormなので、いつ終了するかわかりません。
    私の技術では、廃棄中にInvokeしないことを保証できないんです。

    で、ここに「そんなことねーよ」とツッコミが欲しかったのですが、
    えムナウさんのやりかたでもダメなんですよね。
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

■6857  Re[3]: Control.Invokeが使えない件。
□投稿者/ NyaRuRu -(2007/08/25(Sat) 04:41:56)
    2007/08/25(Sat) 04:47:26 編集(投稿者)

    No6849 (れい さん) に返信
    > 環境とかで問題が発生する確率が大きく変わるので頻度は参考程度ですが、
    > http://bbs.wankuma.com/index.cgi?mode=al2&namber=6760 のコードでは
    > デッドロックが起こりえることは間違いありません。

    確かに http://bbs.wankuma.com/index.cgi?mode=al2&namber=6760 のコードでフォーム2を閉じるとハングすることがあるのは再現しました.
    が,アンマネージデバッグを有効にするで Win32 例外を一通り眺めてみると,ハング時にはファイナライザスレッドから RichEdit に飛んで COM リソースの解放をしているっぽくて,そこでアクセス違反が起きてましたよ.
    以下コールスタックです.

    riched20.dll!CNotifyMgr::Remove() + 0x5 bytes
    riched20.dll!CTxtRange::~CTxtRange() + 0x2d bytes
    riched20.dll!CTxtRange::`scalar deleting destructor'() + 0xd bytes
    riched20.dll!CTxtRange::Release() + 0x1a bytes
    mscorwks.dll!ReleaseTransitionHelper() + 0x5f bytes
    mscorwks.dll!SafeReleaseHelper() + 0x6d bytes
    mscorwks.dll!SafeRelease() + 0x2f bytes
    mscorwks.dll!RCW::ReleaseAllInterfaces() + 0x53 bytes
    mscorwks.dll!RCW::ReleaseAllInterfacesCallBack() + 0x96 bytes
    mscorwks.dll!RCW::Cleanup() + 0x22 bytes
    mscorwks.dll!RCWCleanupList::ReleaseRCWListRaw() + 0x14 bytes
    mscorwks.dll!RCWCleanupList::ReleaseRCWListInCorrectCtx() + 0x4380 bytes
    mscorwks.dll!RCWCleanupList::CleanupAllWrappers() + 0x10e733 bytes
    mscorwks.dll!SyncBlockCache::CleanupSyncBlocks() - 0x5653c bytes
    mscorwks.dll!Thread::DoExtraWorkForFinalizer() + 0xc9d13 bytes
    mscorwks.dll!WKS::GCHeap::FinalizerThreadWorker() + 0xa8 bytes
    mscorwks.dll!Thread::UserResumeThread() - 0x204ae2 bytes
    mscorwks.dll!Thread::DoADCallBack() - 0x203b25 bytes
    mscorwks.dll!Thread::DoADCallBack() - 0x203c00 bytes
    mscorwks.dll!ManagedThreadBase_NoADTransition() + 0x32 bytes
    mscorwks.dll!ManagedThreadBase::FinalizerBase() + 0xb bytes
    mscorwks.dll!WKS::GCHeap::FinalizerThreadStart() + 0xa9 bytes
    mscorwks.dll!Thread::intermediateThreadProc() + 0x46 bytes
    kernel32.dll!@BaseThreadInitThunk@12() + 0x12 bytes
    ntdll.dll!__RtlUserThreadStart@8() + 0x27 bytes

    ちょっと月末締めの原稿書きが忙しいのでこれ以上調べるつもりはないですが,sos.dll 等を使えばその「デッドロック時」に何が起きているかもうちょっと調べられるはずなので,せめて待機中のハンドルが何であるかとか前後で変な (アンマネージ) 例外が起きていないかとか,調べた方がいいんじゃないですかね?
記事No.6843 のレス /過去ログ18より / 関連記事表示
削除チェック/

次の20件>

<< 0 | 1 | 2 >>

パスワード/

- Child Tree -