これには正当な(または少なくともまともな)理由があると確信しています。それは何ですか?
7 に答える
これは素晴らしい質問だと思います - そしてもっと良い答えが必要だと思います.
確かに唯一の理由は、フレームワークのどこかにスレッドセーフではない何かがあるということです。
その「何か」は、System.Windows.Forms のすべてのコントロールのほぼすべてのインスタンス メンバーです。
System.Windows.Forms の多くのコントロールに関する MSDN ドキュメントには、すべてではないにしても、 「この型の public static (Visual Basic では共有) メンバーはスレッド セーフです。インスタンス メンバーは、スレッド セーフであるとは限りません。」と記載されています。
これは、 などのインスタンス メンバーが再入可能TextBox.Text {get; set;}
でないことを意味します。
これらのインスタンス メンバーのそれぞれをスレッド セーフにすると、ほとんどのアプリケーションが必要としない多くのオーバーヘッドが発生する可能性があります。代わりに、.Net フレームワークの設計者は、複数のスレッドからフォーム コントロールへのアクセスを同期させるという負担をプログラマーに課すべきであると判断しました。これは正しいと思います。
[編集]
この質問は「理由」のみを尋ねますが、「方法」を説明する記事へのリンクは次のとおりです。
方法: MSDNの Windows フォーム コントロールへのスレッド セーフな呼び出しを行う
簡単にデッドロックに陥る可能性があるため (他の問題の中でも)。
たとえば、セカンダリ スレッドが UI コントロールを更新しようとしている可能性がありますが、UI コントロールはセカンダリ スレッドによってロックされているリソースが解放されるのを待っているため、両方のスレッドが互いに終了するのを待つことになります。他の人がコメントしているように、この状況は UI コードに固有のものではありませんが、特に一般的です。
C++ などの他の言語では、(WinForms のように例外がスローされることなく) これを自由に試すことができますが、デッドロックが発生した場合、アプリケーションがフリーズして応答を停止する可能性があります。
ちなみに、コントロールを更新することを UI スレッドに簡単に伝えることができます。デリゲートを作成し、そのコントロールで (非同期の) BeginInvoke メソッドを呼び出してデリゲートを渡します。例えば
myControl.BeginInvoke(myControl.UpdateFunction);
これは、ワーカー スレッドから C++/MFC PostMessage を実行するのと同じです。
1.0/1.1 では、デバッグ中に例外はスローされませんでしたが、代わりに、断続的なランタイム ハング シナリオが発生しました。良い!:) したがって、2.0 では、このシナリオで例外がスローされるようになりました。
これの実際の理由はおそらく (Adam Haile が述べているように) ある種の並行性/ロックの問題です。通常の .NET API (TextBox.Text = "Hello"; など) は SEND コマンド (即時のアクションが必要) をラップすることに注意してください。これは、更新を実行するスレッドとは別のスレッドで実行すると問題が発生する可能性があります。Invoke/BeginInvoke を使用すると、アクションをキューに入れる代わりに POST が使用されます。
SEND と POST の詳細については、こちらをご覧ください。
同時に呼び出されることに敏感な更新関数内に同期を実装する必要もあります。UI 要素に対してこれを行うと、アプリケーション レベルと OS レベルの両方でコストがかかり、大部分のコードでは完全に冗長になります。
一部の API は、システムの現在のスレッド所有権を変更する方法を提供するため、スレッド間通信に頼ることなく、他のスレッドからシステムを一時的 (または永続的) に更新できます。
これは、コントロールを同時に更新しようとする 2 つのことがないようにするためです。(これは、書き込み/読み取りの途中で CPU が他のスレッドに切り替わった場合に発生する可能性があります) 複数のスレッド間で共有変数にアクセスするときにミューテックス (またはその他の同期) を使用する必要があるのと同じ理由です。
編集:
C++ などの他の言語では、(WinForms のように例外がスローされることなく) これを自由に試すことができますが、難しい方法を学ぶことになります!
ああ、はい...私はC / C ++とC#を切り替えたので、本来あるべきだったよりももう少し一般的でした。申し訳ありません...彼は正しいです。C / C ++でこれを行うことができますが、あなたをかみます!
うーん、よくわかりませんが、待機バー、進行状況バーなどの進行状況コントロールがある場合、別のスレッドから値を更新でき、すべてが問題なくうまく機能すると思います。