3

に関するこの Apple のドキュメントからNSCondition、の使用法は次のようにNSConditionなります。

スレッド 1:

[cocoaCondition lock];
while (timeToDoWork <= 0)
    [cocoaCondition wait];
timeToDoWork--;
// Do real work here.
[cocoaCondition unlock];

スレッド 2:

[cocoaCondition lock];
timeToDoWork++;
[cocoaCondition signal];
[cocoaCondition unlock];

そして、メソッドのドキュメントsignalNSConditon

このメソッドを使用して、条件で待機している 1 つのスレッドをウェイクアップします。このメソッドを複数回呼び出して、複数のスレッドを起動することができます。条件で待機しているスレッドがない場合、このメソッドは何もしません。競合状態を回避するには、レシーバーがロックされているときにのみこのメソッドを呼び出す必要があります

私の質問は:

どのような状況でもThread 2がブロックされたくないので、 Thread 2を削除しlockunlock呼び出します。つまり、スレッド 2は好きなだけ作業を行うことができ、スレッド 1はその作業を 1 つずつ実行し、それ以上作業がなければ待機 (ブロック) します。これも生産者と消費者のパターンですが、生産者がブロックされることはありません。

しかし、Apple のドキュメントによると、この方法は正しくありません。では、このパターンで何がうまくいかないのでしょうか? ありがとう。

4

1 に答える 1

2

複数のスレッドが共有データにアクセスしている場合、ロックの失敗は深刻な問題です。Apple のコードの例では、スレッド 2 が条件オブジェクトをロックしない場合timeToDoWork、スレッド 1 がデクリメントしているのと同時にインクリメントしている可能性があります。その結果、これらの操作のいずれかの結果が失われる可能性があります。例えば:

スレッド 1 は の現在の値を読み取り、timeToDoWork1 を取得
します スレッド 2 は の現在の値を読み取り、 1timeToDoWorkを取得
します スレッド 2 は増分値 ( timeToDoWork+ 1) を
計算し、 2 を取得しtimeToDoWorkます
の新しい値timeToDoWork、2 を格納
スレッド 1 が の新しい値を書き込み、timeToDoWork0 を格納

timeToDoWork1 から始まり、インクリメントとデクリメントされたので、1 で終わるはずが、実際には 0 で終わります。ステップを並べ替えると、代わりに 2 で終わる可能性があります。おそらく、 の値はtimeToDoWork現実的で重要なものを表しています。それを間違えると、プログラムが台無しになる可能性があります。

2 つのスレッドが数値のインクリメントとデクリメントのような単純なことを行っている場合、 や などのアトミック操作関数を使用して、ロックなしで実行できOSAtomicIncrement32Barrier()ますOSAtomicDecrement32Barrier()。ただし、共有データがそれよりも複雑な場合 (そして、おそらく重要な場合)、条件ロックなどの同期メカニズムを実際に使用する必要があります。

于 2015-01-16T05:17:12.363 に答える