Cの関数内で静的変数を宣言するということは、この変数が関数の呼び出し間でその状態を保持することを意味することを私は知っています。スレッドのコンテキストでは、これにより、変数は複数のスレッドにわたってその状態を保持しますか、それとも各スレッド間で個別の状態を持ちますか?
これが私が答えるのに苦労している過去の紙の試験の質問です:
次のC関数は、呼び出し元に一意の識別子(UID)を割り当てるために使用することを目的としています:
get_uid() { static int i = 0; return i++; }
get_uid()が複数のスレッドによって呼び出されている環境でどのように正しく機能しないかを説明します。特定のシナリオ例を使用して、そのような誤った動作が発生する理由と方法について具体的に説明します。
現時点では、各スレッドが変数に対して個別の状態を持っていると想定していますが、それが正しいかどうか、または答えが相互排除の欠如に関係しているのかどうかはわかりません。その場合、この例でセマフォをどのように実装できますか?
あなたの仮定(スレッドには独自のコピーがあります)は正しくありません。コードの主な問題は、複数のスレッドがその関数get_uid()
を呼び出す場合、どのスレッドがi
をインクリメントし、そうでない可能性のあるIDを取得するかについて競合状態の可能性があることです。一意であること。
プロセスのすべてのスレッドは、同じアドレス空間を共有します。 i
は静的変数であるため、アドレスは固定されています。その「状態」は、そのアドレスのメモリの内容であり、すべてのスレッドで共有されます。
接尾辞++
演算子は引数をインクリメントし、インクリメント前の引数の値を生成します。これらが行われる順序は定義されていません。可能な実装の1つは
copy i to R1
copy R1 to R2
increment R2
copy R2 to i
return R1
複数のスレッドが実行されている場合は、両方がこれらの命令を同時に実行することも、散在させることもできます。さまざまな結果が得られるシーケンスを自分で計算します。 (同じCPUで実行されているスレッドの場合でも、スレッドが切り替えられるとレジスタが保存および復元されるため、各スレッドには独自のレジスタ状態があることに注意してください。)
異なるスレッドでの操作の不確定な順序によって結果が異なるこのような状況は、競合状態と呼ばれます。これは、異なるスレッド間で、どちらが最初にどの操作を実行するかについて「競合」があるためです。 。
いいえ、使用されているスレッドに応じて値が異なる変数が必要な場合は、 スレッドローカルストレージ を確認する必要があります。
静的変数。完全にグローバルな変数のように想像できます。それは本当にほとんど同じです。したがって、アドレスを知っているシステム全体で共有されます。
[〜#〜] edit [〜#〜]:コメントとしても、この実装を静的変数として保持すると、競合状態が発生する可能性があります。値i
が複数のスレッドによって同時にインクリメントされるようにします。これは、関数呼び出しによって返される値がわからないことを意味します。このような場合は、 ミューテックス や クリティカルセクション などのいわゆる同期オブジェクトによってアクセスを保護する必要があります。
Gccを使用している場合は、 アトミックビルトイン 関数を使用できます。他のコンパイラで何が利用できるかわかりません。
int get_uid()
{
static int i = 0;
return __atomic_fetch_add(&i, 1, __ATOMIC_SEQ_CST);
}
これにより、一度に複数のスレッドが変数を操作できないようになります。
これは宿題のように見えるので、これの一部だけに答えます。つまり、各スレッドはi
の同じコピーを共有します。 IOW、スレッドは独自のコピーを取得しません。相互排除ビットはあなたにお任せします。
各スレッドは同じ静的変数を共有しますが、これはほとんどの場合グローバル変数です。一部のスレッドの値が間違っている可能性があるシナリオは、競合状態です(インクリメントは、1回の実行ではなく、3つのアセンブリ命令、ロード、インクリメント、ストアで実行されます)。ここを読んでください、そして、リンクの図はそれをよく説明します。