7

私は自分のアプリケーション(単純なwinform)にあるいくつかのListBoxにいくつかの文字列を追加しようとしています - そして私はBeginInbokeを使ってそれをしました

 myListBox.BeginInvoke(new Action(delegate()
 {
       myListBox.Items.Add( "some string" )); 
 }));

これらの 3 行をもう一度読んだ後、Google と MSDN で見たクロス スレッド UI の例で EndInvoke の呼び出しが表示されない理由がわかりません。この場合、EndInvoke を呼び出さない理由はありますか?

4

2 に答える 2

17

これは、.NET での残念な命名の選択でした。Control.BeginInvoke および Dispatcher.BeginInvoke メソッドはデリゲートのメソッドと同じ名前ですが、動作はまったく異なります。主な違い:

  • デリゲートの BeginInvoke() メソッドは常にタイプ セーフであり、デリゲート宣言とまったく同じ引数を持ちます。これは Control/Dispatcher バージョンには完全にありません。引数は object[] 型の params 配列を介して渡されます。引数が間違っている場合、コンパイラは通知しません。実行時に爆撃します。

  • デリゲートの Invoke() メソッドは、同じスレッドでデリゲート ターゲットを実行します。Control/Dispatcher.Invoke() の場合ではなく、UI スレッドへの呼び出しをマーシャリングします。

  • デリゲートの BeginInvoke() ターゲットでスローされた例外はキャプチャされ、プログラムが失敗することはありません。EndInvoke() を呼び出したときに再スローされます。Control/Dispatcher.BeginInvoke() の場合はまったく当てはまりません。UI スレッドで例外が発生します。例外をキャッチする適切な方法がないため、Application.UnhandledException が存在する大きな理由の 1 つです。

  • デリゲートの EndInvoke() メソッドを呼び出す必要があります。そうしないと、10 分間のリソース リークが発生します。Control/Dispatcher.BeginInvoke() メソッドでは必須ではなく、実際にはそうする必要はありません。

  • Control/Dispatcher.Invoke() の使用は危険であり、デッドロックを引き起こす可能性が非常に高くなります。UI スレッドがターゲットを呼び出す準備ができておらず、スレッドの完了を待機するなどの賢明でない処理を行った場合にトリガーされます。Invoke() メソッドがスレッドを使用しないため、少なくともデリゲートの問題ではありません。

  • UI スレッドでの Control/Dispatcher.BeginInvoke() の呼び出しは、サポートされているシナリオです。予想どおり、ターゲットは引き続き UI スレッドで実行されます。しかし、その後、UI スレッドが再びアイドル状態になり、ディスパッチャー ループに再び入ります。これは実際には非常に便利な機能であり、トリッキーな再入可能性の問題を解決するのに役立ちます。特に、副作用が多すぎるコードを実行すると誤動作する UI コントロールのイベント ハンドラーで。

実装の詳細が記載された大きなリスト。TLDR バージョンは確かに、「それらには共通点がなく、EndInvoke を呼び出さないことは問題なく、完全に正常です」です。

于 2013-08-02T14:31:12.873 に答える
5

Control.BeginInvoke非同期プログラミング モデル (APM)BeginXとも呼ばれる通常の/EndXパターンに完全に従っているようには見えません。通常、for eachを呼び出す必要がありますが、 の場合、これは厳密には必須ではありません。EndXBeginXControl.BeginInvoke

「必要に応じて、デリゲートから戻り値を取得するために呼び出すことができますEndInvokeが、これは必須ではありません。戻り値を取得できるようになるまで、EndInvoke はブロックされます。」

— MSDNリファレンスページControl.BeginInvokeの備考セクションから(強調は私による)

そして実際には、それが必要になることはほとんどありません。これは、メソッドが通常、UI を更新する UI スレッドでコードを実行できるようにするために呼び出されるためです。通常、UI を更新しても戻り値は生成されないため、 を呼び出す必要はありませんEndInvoke

于 2013-08-02T12:07:31.907 に答える