最近、(UIスレッド以外の)スレッドからテキストボックスにアクセスしようとすると、例外がスローされました。 「コードがスレッドセーフではない」ことについて何かを言っていたので、最終的にデリゲート(MSDNのサンプルが役立ちました)を書いて呼び出しました。
しかし、それでも私はなぜすべての余分なコードが必要なのかよく理解していませんでした。
更新:チェックすると深刻な問題が発生しますか
Controls.CheckForIllegalCrossThread..blah =true
Eric Lippert という素敵なブログ投稿があります 「スレッドセーフ」と呼ぶこのことは何ですか? ウィキペディアで見つかったスレッドセーフの定義について。
リンクから抽出された3つの重要な事項:
「コードの一部は、複数のスレッドによる同時実行中に正しく機能する場合、スレッドセーフです。」
「特に、複数のスレッドが同じ共有データにアクセスする必要性を満たす必要があります...」
「...そして、共有データの一部が常に1つのスレッドのみによってアクセスされる必要性。」
間違いなく読む価値があります!
簡単に言うと、スレッドセーフとは、複数のスレッドから安全にアクセスできることを意味します。プログラムで複数のスレッドを使用していて、それぞれが共通のデータ構造またはメモリ内の場所にアクセスしようとすると、いくつかの悪いことが起こります。そのため、これらの悪いことを防ぐためにいくつかのコードを追加します。たとえば、2人が同じドキュメントを同時に書いている場合、保存する2番目の人が最初の人の作業を上書きします。スレッドセーフにするには、ユーザー2がドキュメントを編集できるようにする前に、ユーザー1がタスク1を完了するまで待機するようにユーザー2に強制する必要があります。
Wikipedia にはスレッドセーフティに関する記事があります。
この 定義ページ (広告をスキップする必要があります-申し訳ありませんが)このように定義しています:
コンピュータープログラミングでは、スレッドセーフとは、複数のプログラミングスレッドから、スレッド間の不要な対話なしに呼び出すことができるプログラム部分またはルーチンを指します。
スレッドは、プログラムの実行パスです。シングルスレッドプログラムにはスレッドが1つしかないため、この問題は発生しません。実質的にすべてのGUIプログラムには複数の実行パスがあり、したがってスレッドがあります。GUIの表示を処理し、ユーザー入力を処理するための少なくとも2つと、プログラムの操作を実際に実行するための少なくとも1つです。
これは、実行中のプロセスを非UIスレッドにオフロードすることにより、プログラムの動作中にUIが応答するようにするためです。これらのスレッドは、一度作成されてプログラムの存続期間中存在するか、または必要に応じて作成され、終了時に破棄されます。
これらのスレッドは通常、ディスクI/O、画面への結果の出力など、一般的なアクションを実行する必要があるため、コードのこれらの部分は、多くの場合、複数のスレッドからの呼び出しを処理できるように記述する必要があります同じ時間。これには次のようなものが含まれます。
簡単に言うと、スレッドセーフとは、問題が発生することなく、メソッドまたはクラスインスタンスを複数のスレッドで同時に使用できることを意味します。
次の方法を検討してください。
private int myInt = 0;
public int AddOne()
{
int tmp = myInt;
tmp = tmp + 1;
myInt = tmp;
return tmp;
}
ここで、スレッドAとスレッドBの両方がAddOne()を実行しようとしています。ただし、Aが最初に開始し、myInt(0)の値をtmpに読み取ります。何らかの理由で、スケジューラーはスレッドAを停止し、スレッドBへの実行を延期することを決定しました。スレッドBは、myInt(まだ0)の値も独自の変数tmpに読み込みます。スレッドBはメソッド全体を終了するため、最終的にmyInt = 1になり、1が返されます。スレッドAの番です。スレッドAは続行します。そして、tmpに1を追加します(スレッドAの場合、tmpは0でした)。そして、この値をmyIntに保存します。 myIntは再び1です。
したがって、この場合、メソッドAddOneが2回呼び出されましたが、メソッドがスレッドセーフな方法で実装されていないため、myIntの値は予想どおり2ではなく、1番目のスレッドが終了する前に2番目のスレッドが変数myIntを読み取ったためですそれを更新します。
スレッドセーフなメソッドの作成は、些細なケースでは非常に困難です。そして、かなりの数のテクニックがあります。 Javaメソッドを同期済みとしてマークできます。これは、特定の時間に1つのスレッドのみがそのメソッドを実行できることを意味します。他のスレッドはインラインで待機します。メソッドで多くの作業が必要な場合、これは多くのスペースを浪費します別のテクニックは「メソッドのごく一部のみを同期済みとしてマークする」ロックまたはセマフォを作成することですこの小さな部分をロックします(通常、クリティカルセクションと呼ばれます)。ロックレススレッドセーフとして実装されているメソッドもあります。つまり、複数のスレッドが同時にそれらを介して競合できるように構築されています。これは、問題が発生することなく、メソッドが1つのアトミックコールのみを実行する場合に当てはまります。
モジュールは、マルチスレッドおよび同時使用に直面しても不変式を維持できることが保証されている場合、スレッドセーフです。
ここで、モジュールはデータ構造、クラス、オブジェクト、メソッド/プロシージャ、または関数です。基本的にスコープされたコードと関連データ。
保証は、特定のCPUアーキテクチャなどの特定の環境に限定される可能性がありますが、それらの環境では保持する必要があります。環境の明示的な区切りがない場合、通常は、すべての環境でコードをコンパイルおよび実行できることを意味すると解釈されます。
スレッドセーフでないモジュールmayは、マルチスレッドおよび同時使用で正しく機能しますが、これは多くの場合、慎重な設計よりも運と偶然によるものです。一部のモジュールがあなたのために壊れていない場合でも、他の環境に移動すると壊れる可能性があります。
マルチスレッドのバグはしばしばデバッグが困難です。それらのいくつかはたまにしか発生しませんが、他のいくつかは積極的に現れます-これも環境に固有のものです。それらは微妙に間違った結果、またはデッドロックとして現れる可能性があります。予測不可能な方法でデータ構造を台無しにし、コードの他のリモート部分に、一見不可能な他のバグを出現させる可能性があります。非常にアプリケーション固有である可能性があるため、一般的な説明をするのは困難です。
素人の実世界の例は
インターネットとモバイルバンキングの銀行口座があり、口座が10ドルしかないとします。モバイルバンキングを使用して別の口座への振替残高を実行し、その間、同じ銀行口座を使用してオンラインショッピングを行いました。この銀行口座がスレッドセーフでない場合、銀行は2つのトランザクションを同時に実行することを許可し、その後銀行は破産します。
スレッドセーフとは、複数のスレッドが同時にオブジェクトにアクセスしようとしても、オブジェクトの状態が変わらないことを意味します。
「Java Concurrency in Practice」という本からより多くの説明を得ることができます:
クラスは、ランタイム環境によるスレッドの実行のスケジューリングまたはインターリーブに関係なく、複数のスレッドからアクセスされたときに正しく動作し、呼び出しコード側で追加の同期または他の調整なしで、スレッドセーフです。
スレッドセーフティ:スレッドセーフプログラムは、メモリの一貫性エラーからデータを保護します。高度にマルチスレッド化されたプログラムでは、スレッドセーフプログラムは、同じオブジェクト上の複数のスレッドからの複数の読み取り/書き込み操作による副作用を引き起こしません。異なるスレッドは、一貫性エラーなしでオブジェクトデータを共有および変更できます。
高度な同時実行性APIを使用して、スレッドセーフを実現できます。このドキュメント page は、スレッドセーフを実現するための優れたプログラミング構造を提供します。
Lock Objects 多くの同時アプリケーションを簡素化するロックイディオムをサポートします。
Executors スレッドを起動および管理するための高レベルAPIを定義します。 Java.util.concurrentによって提供されるエグゼキューター実装は、大規模アプリケーションに適したスレッドプール管理を提供します。
Concurrent Collections 大量のデータコレクションの管理を容易にし、同期の必要性を大幅に削減できます。
原子変数 には、同期を最小限に抑え、メモリの一貫性エラーを回避する機能があります。
ThreadLocalRandom(JDK 7)は、複数のスレッド。
他のプログラミング構成については、 Java.util.concurrent および Java.util.concurrent.atomic パッケージも参照してください。
http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 の概念は、メソッドがサイドを持ち、依存している場合の安全でないスレッドと通常考えるものですグローバル変数などの効果。
たとえば、浮動小数点数を文字列にフォーマットしたコードを見ました。これらの2つが異なるスレッドで実行されている場合、decimalSeparatorのグローバル値は '。'に永続的に変更できます。
//built in global set to locale specific value (here a comma)
decimalSeparator = ','
function FormatDot(value : real):
//save the current decimal character
temp = decimalSeparator
//set the global value to be
decimalSeparator = '.'
//format() uses decimalSeparator behind the scenes
result = format(value)
//Put the original value back
decimalSeparator = temp
WinForms環境で作業していることは明らかです。 WinFormsコントロールはスレッドアフィニティを示します。つまり、コントロールが作成されたスレッドだけが、それらにアクセスして更新することができます。そのため、MSDNや他の場所で、メインスレッドにコールバックをマーシャリングする方法を示す例を見つけることができます。
通常のWinFormsの実践では、すべてのUI作業専用の単一のスレッドを使用します。