に一つだけの書き込みで、アップへの日付値インターロック変数からを読む:私は2つのメソッドを持つクラスを作成したい変数
void SetValue(T value)
店舗値が、一つだけを保存することができますそれ以外の場合は例外がスローされます。T GetValue()
は値を取得します(まだ値がない場合は例外をスローします)。
私は、次の欲望/制約がある:値を読み込む
- は安価でなければなりません。
- 値を書くことは(適度に)高価なことがあります。それは別のスレッドで
SetValue()
への呼び出し後に古いnull
値に基づいて例外をスローしないでください。 GetValue()
は、最新の値が(null
)存在しない場合にのみ、例外をスローする必要があります。- 値は1回だけ書き込まれます。これは、
GetValue()
がヌルでない場合、値をフレッシュにする必要がないことを意味します。 - 完全なメモリバリアが回避できれば、それは(ずっと)良いことです。
- ロックフリーの並行処理が優れていますが、ここに該当するかどうかはわかりません。
私はこれを達成するためのいくつかの方法を考え出しましたが、どちらが正しいか、効率的か、正しいか、効率的か、そしてより良い方法があるかはわかりません私が望むものを達成する。これは、(おそらく間違っている)を前提に依存しているフィールド
Interlocked.CompareExchange
を使用したフィールドInterlocked.CompareExchange
を使用し方法1
- フィールド上で
Interlocked.CompareExchange(ref v, null, null)
を実行すると、次のアクセスには、少なくともInterlocked.CompareExchange
が見たものと同じくらい新しい値が得られます。
コード:
public class SetOnce1<T> where T : class
{
private T _value = null;
public T GetValue() {
if (_value == null) {
// Maybe we got a stale value (from the cache or compiler optimization).
// Read an up-to-date value of that variable
Interlocked.CompareExchange<T>(ref _value, null, null);
// _value contains up-to-date data, because of the Interlocked.CompareExchange call above.
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
}
// _value contains up-to-date data here too.
return _value;
}
public T SetValue(T newValue) {
if (newValue == null) {
throw new System.ArgumentNullException();
}
if (Interlocked.CompareExchange<T>(ref _value, newValue, null) != null) {
throw new System.Exception("Value already present.");
}
return newValue;
}
}
方法2
- Ìnterlocked.CompareExchange
to write the value (with [Joe Duffy](http://www.bluebytesoftware.com/blog/PermaLink,guid,c36d1633-50ab-4462-993e-f1902f8938cc.aspx)'s
の#pragmato avoid the compiler warning on passing a volatile value by
ref`を使用volatile
フィールド - を使用)。
- フィールドは
volatile
コードであるため、値を直接読み取り:非揮発性フィールドを使用
public class SetOnce2<T> where T : class
{
private volatile T _value = null;
public T GetValue() {
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
return _value;
}
public T SetValue(T newValue) {
if (newValue == null) {
throw new System.ArgumentNullException();
}
#pragma warning disable 0420
T oldValue = Interlocked.CompareExchange<T>(ref _value, newValue, null);
#pragma warning restore 0420
if (oldValue != null) {
throw new System.Exception("Value already present.");
}
return newValue;
}
}
方法3
- 。
- 書き込み時にロックを使用する。
- nullを読み取ると、読み取り時にロックを使用します(参照がアトミックに読み取られるため、ロックの外側に一貫した値が得られます)。ロックを使用して値を書き込む
- 揮発性フィールドを使用
public class SetOnce3<T> where T : class { private T _value = null; public T GetValue() { if (_value == null) { // Maybe we got a stale value (from the cache or compiler optimization). lock (this) { // Read an up-to-date value of that variable if (_value == null) { throw new System.Exception("Value not yet present."); } return _value; } } return _value; } public T SetValue(T newValue) { lock (this) { if (newValue == null) { throw new System.ArgumentNullException(); } if (_value != null) { throw new System.Exception("Value already present."); } _value = newValue; return newValue; } } }
方法4
- :
コード。
- フィールドがvolatileであるため、値を直接読み込みます(参照がアトミックに読み取られるため、ロックを使用しない場合でもコヒーレントな値が得られます)。
コード:
public class SetOnce4<T> where T : class
{
private volatile T _value = null;
public T GetValue() {
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
return _value;
}
public T SetValue(T newValue) {
lock (this) {
if (newValue == null) {
throw new System.ArgumentNullException();
}
if (_value != null) {
throw new System.Exception("Value already present.");
}
_value = newValue;
return newValue;
}
}
}
他の方法
私も執筆技術のいずれかと組み合わせて、値を読み取るためにThread.VolatileRead()
を使用することができます。
あなたが '' ReaderWriterLock(スリム)を使用して考えがありますか?単純な 'ロック 'があなたのニーズを満たしていないと確信していますか?何故なの? 'SetOnce'クラスの使用パターンは何ですか? – dtb
私は 'ReaderWriterLock'を忘れてしまったので、私はそれをロックします。しかし、通常の 'lock'と同じように、読み込みごとに同期のオーバーヘッドが導入されると思います。なぜなら値は一度しか変更されないので避けたいのです。つまり、null以外の値(ロックなし)それが失効していないことを保証する。 –
ReaderWriterLock(Slim)は、複数の同時読み取りが可能なため、実際には順序ロックより優れています。 – Woodman