22

locka will がフィールドからの値を強制的にリロードすることは一般的に受け入れられています (私は信じています!)。内でのみアクセスされるフィールド自体は である必要はありませlockvolatile

(私がすでに間違っている場合は、言ってください!)

良いコメントがここで提起され、コードが a を行う場合に同じことが当てはまるかどうかを疑問視しWait()ていPulse()ます。

またはもっと簡単に: フィールドはvolatileWait()?

リフレクタを見て、(と同じ )である をWait呼び出します。ObjWaitmanaged internalcallEnter

問題のシナリオは次のとおりです。

bool closing;
public bool TryDequeue(out T value) {
    lock (queue) { // arbitrary lock-object (a private readonly ref-type)
        while (queue.Count == 0) {
            if (closing) {       // <==== (2) access field here
                value = default(T);
                return false;
            }
            Monitor.Wait(queue); // <==== (1) waits here
        }
        ...blah do something with the head of the queue
    }
}

明らかに、単にそれを作成することも、パルスが発生するたびvolatileに終了して再入力するようにこれを移動することもできますが、どちらかが必要かどうかを知りたいと思っています.Monitor

4

3 に答える 3

18

Wait()メソッドはMonitorロックの解放と再取得を行っているため、メモリ フェンス セマンティクスを実行する場合もlock同様Monitor.Wait()です。

うまくいけばあなたのコメントに対処するには:

のロック動作はMonitor.Wait()ドキュメント ( http://msdn.microsoft.com/en-us/library/aa332339.aspx ) にあり、強調が追加されています。

スレッドが Wait を呼び出すと、オブジェクトのロックが解除され、オブジェクトの待機キューに入ります。オブジェクトの準備完了キュー (存在する場合) 内の次のスレッドがロックを取得し、オブジェクトを排他的に使用します。を呼び出すすべてのスレッドは、ロックの所有者から送信されたWaitPulse または からシグナルを受信するまで待機キューに残ります。PulseAllが送信された場合Pulse、待機キューの先頭にあるスレッドのみが影響を受けます。がPulseAll送信されると、オブジェクトを待機しているすべてのスレッドが影響を受けます。シグナルが受信されると、1 つ以上のスレッドが待機キューを離れ、準備完了キューに入ります。レディ キュー内のスレッドは、ロックを再取得できます。

このメソッドは、呼び出し元のスレッドがオブジェクトのロックを再取得すると戻ります

lock/acquiredMonitorがメモリバリアを意味するかどうかの参照について質問している場合、 ECMA CLI 仕様には次のように記載されています。

12.6.5 ロックとスレッド:

ロックを取得する (System.Threading.Monitor.Enterまたは同期メソッドに入る) と、揮発性読み取り操作が暗黙的に実行され、ロックを解放する (System.Threading.Monitor.Exitまたは同期メソッドから離れる) と、揮発性書き込み操作が暗黙的に実行されます。§12.6.7 を参照してください。

12.6.7 揮発性の読み取りと書き込み:

揮発性読み取りには「取得セマンティクス」があります。これは、CIL 命令シーケンスの読み取り命令の後に発生するメモリへの参照の前に読み取りが発生することが保証されることを意味します。揮発性書き込みには「解放セマンティクス」があります。つまり、書き込みは、CIL 命令シーケンスの書き込み命令の前のメモリ参照の後に発生することが保証されます。

また、これらのブログ エントリには、興味深い情報がいくつか含まれています。

于 2010-03-12T08:09:35.027 に答える
4

Michael Burr の回答にWait加えて、ロックを解放して再取得するだけでなく、別のスレッドが共有状態を調べて を呼び出すためにロックを取得できるようにするためPulseです。2 番目のスレッドがロックを取得しない場合は、Pulseスローします。そうしないPulseと、最初のスレッドWaitは返されません。したがって、共有状態への他のスレッドのアクセスは、適切なメモリ制限シナリオ内で発生する必要があります。

Monitorしたがって、メソッドがローカルでチェック可能なルールに従って使用されていると仮定すると、すべてのメモリアクセスはロック内で発生するため、の自動メモリバリアサポートのみlockが関連/必要です。

于 2010-03-12T08:15:22.827 に答える
1

たぶん私は今回あなたを助けることができます...あなたが整数でvolatile使うことができるを使う代わりに。Interlocked.Exchange

if (closing==1) {       // <==== (2) access field here
    value = default(T);
    return false;
}

// somewhere else in your code:
Interlocked.Exchange(ref closing, 1);

Interlocked.Exchangeは同期メカニズムですが、そうでvolatileはありません...それが何か価値があることを願っています(しかし、おそらくあなたはすでにこれについて考えています)。

于 2010-03-12T08:26:20.930 に答える