元のコードの問題点を改めて追ってみるが、ここからは話が複雑になる。自信のない人は、前節の解答だけを頭に入れておいてほしい。前述したように、挙げたコードでほとんどの場合、用が足りるはずだ。
最初の問題は、マイクロソフト自身が当初間違えていたことによって、多くの人が間違えているようだ。この行を見てほしい。
lock (typeof(A)) {
C#とVB.NETにおけるlock/SyncLockの対象は、object、つまり参照型なら何でもよいことになっている。型も参照型であるため、文法的にはこれで正しい。また、Visual Studio付属のSDK中のサンプルにもこの使い方が頻発するため、誰が教えなくても何気なくそのまま書いてしまうプログラマーが多い。
しかし、SDKにこの記法のサンプルを入れてしまったことは間違いであったと米Microsoftの社員がBlogに書いている。
問題は、「typeof(A)」がどこからでもアクセスできることによる。例えば別のプログラマーが別のクラスの中で次のように書いたとしよう。
lock (typeof(A)) {
DoSomething();
}
同じ対象オブジェクトを使い、別の目的でlockをかけてしまえば、デッドロックの危険性が高まる。クラス外からも参照可能なオブジェクトを使ってのlockは危険、と覚えておこう。時折見かけるこのコードも同じ理由で危険だ。
lock (this) {
...
}
「this」は、もちろんほかのクラスから参照されるので、それ自体をlockしてはいけない。この規則に従って元のコードを直してみよう。
public class A {
private static A _Value;
private static object ValueLock =
new object();
public static Value {
get {
lock (ValueLock) {
if (_Value == null)
_Value = new A();
}
Return _Value;
}
}
}
このコードでは、lock用に別の静的変数を確保している。この変数はクラス外からは参照できないため、同じクラス内で間違えない限り、デッドロックの心配がない。
Copyright © ITmedia, Inc. All Rights Reserved.