コードの重要な部分を保護するためにlock ステートメントを使用してきましたが、いくつかの条件が満たされれば、その重要なコードの同時実行を許可できることに気付きました。
ロックを調整する方法はありますか?
8 に答える
その質問は「レースコンディション!」と叫ぶと思います。チェックの直後で、スレッドがコードのクリティカル セクションに入る前に、条件が true から false に変わったらどうなるでしょうか。それとも、スレッドが実行中ですか?
bool locked = false;
if (condition) {
Monitor.Enter(lockObject);
locked = true;
}
try {
// possibly critical section
}
finally {
if (locked) Monitor.Exit(lockObject);
}
EDIT:はい、スレッドが入っている間、条件が一定であることを保証できない限り、競合状態があります。
私はスレッドの専門家ではありませんが、このようなもの (二重チェックのロック) を探しているようです。アイデアは、ロックを取得する前後の状態をチェックすることです。
private static object lockHolder = new object();
if (ActionIsValid()) {
lock(lockHolder) {
if (ActionIsValid()) {
DoSomething();
}
}
}
Action doThatThing = someMethod;
if (condition)
{
lock(thatThing)
{
doThatThing();
}
}
else
{
doThatThing();
}
実際には、競合状態を回避するために、ReaderWriterLockSlim
同時アクセスを読み取りロックとして扱い、排他的アクセスを書き込みロックとして扱います。そうすれば、条件が変わっても、不適切なコードがその領域で盲目的に実行されることはありません (安全であるという誤った仮定の下で)。少し冗長ですが(スペース用にフォーマットされています):
if (someCondition) {
lockObj.EnterReadLock();
try { Foo(); }
finally { lockObj.ExitReadLock(); }
} else {
lockObj.EnterWriteLock();
try { Foo(); }
finally { lockObj.ExitWriteLock(); }
}
次のようなコードがあると思います。
private Monkey GetScaryMonkey(int numberOfHeads){
Monkey ape = null;
lock(this) {
ape = new Monkey();
ape.AddHeads(numberOfHeads);
}
return ape;
}
これを条件付きにするために、これを行うことはできませんでした:
private Monkey GetScaryMonkey(int numberOfHeads){
if ( numberOfHeads > 1 ) {
lock(this) {
return CreateNewMonkey( numberOfHeads );
}
}
return CreateNewMonkey( numberOfHeads );
}
うまくいくはずですよね?
上記のように、ダブルチェック ロック パターンを使用します。それがトリックIMOです:)
not.that.dave.foley.myopenid.com の例に記載されているように、ロック オブジェクトがstaticであることを確認してください。