2つのスレッドの物語

IT企業とのインタビューで、次の質問に答えることが提案されました。

チャレンジ。 次のコードを考えます:

		 static int counter = 0;

		 void worker()
		 {
			 for(int i = 1; i <= 10; i ++)
				カウンター++;
		 }


worker()プロシージャは2つのスレッドから開始されます。 counter変数には、両方のスレッドの最後にどのような値が含まれますか?

ちょっとした理論。 スレッドは、同じプロセス内の並列実行タスクです。 プロセスとスレッドの主な違いは、1つのプロセスのすべてのスレッドがそのプロセスの共通ア​​ドレス空間で動作することです。

実行スレッドthread )とデータストリーム( stream )を混同しないように、スレッドスレッドを呼び出しません。



回答番号1バリエーション。 counter変数には20という数値が含まれます。これは最も望ましい結果ですが、最も誤った回答でもあります。

問題は、このコードが同期解除から保護されていないことです。 各スレッドは互いに独立して動作し、一般データ( counter変数)が他の誰かによって変更される可能性があるとは考えていません。 したがって、より正しい答えはこれです。 これは安全でないコードであり、 counter変数の値はundefinedになります。

オプション番号2。プロセッサ、OS、およびコンパイラの基本をまだ理解しているので、どの結果が得られるかをより正確に推測できます。

worker()ルーチンは非常に単純です。 したがって、最適化の段階で、コンパイラはそのような構成でforループを展開できます。

	カウンター+ = 10; 


これでコードは非常に単純なため、2番目のスレッドが開始する前であっても、最初のスレッドは実行を終了する時間ができます。 したがって、20という数字が得られます。

そのような場合も可能です。 スレッドは、 counter変数(ゼロ)の値をRAMから読み取り、プロセッサーのレジスターに入れます。 各スレッドは独自のプロセッサコンテキストで動作するため、各スレッドには独自のレジスタセットがあります。 したがって、コンテキスト内の最初のスレッドは、レジスタの値を0から10に増やしてから、結果をメモリに戻します。 そして、2番目のスレッドは同じことを行い、10個のスレッドを書き換えます。 その結果、10という数字が得られます。

10と20の値は実際には実際に最も可能性が高いですが、これはまだ完全な答えではありません。

オプション番号3。潜在的に実際の、しかし最大限に効果のない作業スキームを想像してみましょう。 したがって、サイクルの各反復は、3つのアトミックアクションを等しく繰り返します。つまり、 counter値をプロセッサレジスタに読み込み、このレジスタをインクリメントし、レジスタから新しい値をメモリに書き戻します。 (ループは何の役割も果たさないため、ループ自体の実装は考慮しません。)さらに、スレッド実行コンテキストの切り替えは、必要なときに行われます。 このような条件下では、2〜20の結果が得られます。

次に、表形式のタイムラインで、デュースを取得する可能性を検討します。 ステップ0は初期状態です。 「→ n 」、「 n +」、「 n →」の各操作は、スレッド番号nのコンテキストでメモリからレジスタへの読み取り、レジスタのインクリメント、およびレジスタの値のメモリへの書き込みをそれぞれ表します。 行「ループ」は反復回数を示します。 知覚を改善するために、現在のステップの値の変更が強調表示されます。

ステップ012345...272829日303132333435...57585960
運営-→11+→22+2→→22+2→1→→22+→11+1→→11+1→2→
スレッド番号1登録する-01111111111122...9101010
サイクル11111111111122210101010
スレッド番号2登録する---011...8999122222222
サイクル1111119999101010101010101010
記憶00000188911111299102

(追加:読者の要求に応じて、ビットマップ付きのテーブルのバリアントがあります 。)

ステップ1〜2:最初のスレッドがレジスタ0に入り、1増加します。 3-29:制御は2番目のスレッドに転送されます。これもゼロを読み取り、9回の完全な反復を実行します。 30:最初のスレッドはループの最初の反復を完了し、カウントされたユニットをメモリに保存します(9個をラビングします)。 31–32:2番目のスレッドが最後の(10番目の)反復を開始し、ユニットを読み取り、無害にします。 33–59:最初のスレッドは残りの9回の反復を完全に実行し、作業を終了します。 60:2番目のスレッドは、カウントされた2つをメモリに書き込み、作業を終了します。

_________
NB:これは私のブログからのクロスポストです。 Habréの視聴者が私のブログよりも多いため、私はそれを再版しました(:そして、素材は興味深いと思います。

Source: https://habr.com/ru/post/J63697/


All Articles