- PR -

マルチスレッドシングルトンでロックオブジェクトがnull

投稿者投稿内容
頭脳パン
ベテラン
会議室デビュー日: 2003/04/03
投稿数: 89
投稿日時: 2006-08-29 17:33
以下のソースのように、マイクロソフトのマルチスレッドシングルトンそのままのソースを書いたのですが
http://www.microsoft.com/japan/msdn/practices/type/Patterns/enterprise/ImpSingletonInCsharp.asp
(ただし、ソース掲載の為に修正はいれています)

lock (lockObj)のところで、lockObjがnullで例外になってしまいます。
ただ、Aの箇所のように初期化しているのに、nullになってしまうのが
なぜかわかりません。
このクラスをコンソールプログラムにコピーして実行したところ
lockObjは初期化されていました。

どのような場合に、lockObjは初期化されてない状況になるのでしょう?

public sealed class Singleton
{
private static object lockObj = new Object(); // ←A

private static Singleton instance;

private Singleton(string s)
{
}

public static Singleton Instance
{
get
{
if (instance == null)
{
lock (lockObj)
{
if (instance == null)
{
instance = new Singleton("aa");
}
}
}
return instance;
}
}
}
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2006-08-29 21:50
再現するコードはありますか?
_________________
囚人のジレンマな日々
k_kazu
常連さん
会議室デビュー日: 2006/02/11
投稿数: 25
投稿日時: 2006-08-29 21:54
引用:

頭脳パンさんの書き込み (2006-08-29 17:33) より:
以下のソースのように、マイクロソフトのマルチスレッドシングルトンそのままのソースを書いたのですが


volatile 宣言が抜けているのが原因だと思うのですが・・。
同ページに
『変数が volatile として宣言されていることで、インスタンス変数への代入が完了するまでは、インスタンス変数にアクセスできなくなっています。』
と書かれています。
あれ? マイクロソフトのページでは lockObj に volatile 宣言がありませんね?
これが原因で lockObj が NULL になっているのかもしれません。

instance と lockObj 両方に volatile 宣言を追加して検証して
その結果を教えてください。
(できればマルチCPUの環境でテストできるとベスト・・)
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2006-08-29 22:08
ん〜、lockObj には、volatile は必要なさそうなんですけどね。
でも確かに instance には必要かも。というか処理する順序が悪いのかな。
NullReferenceException をスローしてるのは、instance 変数を使ってるどこかかな?
コード:

public sealed class Singleton
{
private static object lockObj = new Object(); // ←A

private static Singleton instance;

private Singleton(string s)
{
}

public static Singleton Instance
{
get
{
if (instance == null) <-(2)
{
lock (lockObj)
{
if (instance == null)
{
instance = new Singleton("aa");
}
}
}
return instance; <-(2)
}
}
}



コード:

public sealed class Singleton
{
private static object lockObj = new Object(); // ←A

private static Singleton instance;

private Singleton(string s)
{
}

public static Singleton Instance
{
get
{
lock (lockObj)
{
if (instance == null)
{
instance = new Singleton("aa");
}
}
return instance;
}
}
}


こうしたら volatile は必要なさそうですが、毎度 lock するのもウザイですね。


_________________
囚人@わんくま同盟
囚人のジレンマな日々

[ メッセージ編集済み 編集者: 囚人 編集日時 2006-08-29 22:10 ]
がんふぃーるど
ベテラン
会議室デビュー日: 2006/06/05
投稿数: 58
お住まい・勤務地: さいたま
投稿日時: 2006-08-30 00:29
最初のコードが一番正しいと思いますよ。
if(instance == null) 〜 lock(lockObj) 〜 if(instance == null)
は有名なイディオムです。(ダブルチェックロッキングだったかな…)

ソースを見る限りinstanceがnullになる原因が見当たらないので、
現象が発生するDLLやEXEに対しILDASMやReflectorを使用し、
問題となるクラスのタイプイニシャライザ(.cctor)にlockObjを
初期化(インスタンス化)するコードがあるかチェックしてみるとか…
(Staticなフィールドの初期化は、タイプイニシャライザの先頭で
行われているはずです。)
k_kazu
常連さん
会議室デビュー日: 2006/02/11
投稿数: 25
投稿日時: 2006-08-30 02:11
気になったので勉強のため、いろいろ調べてみました。
・・でも lockObj が null になる原因は わかりませんでした。

ダブルチェックロッキングは javaでは使わない方が良いと解説した
http://www-06.ibm.com/jp/developerworks/java/020726/j_j-dcl.html
のページが マルチスレッドのシングルトンについての解説が詳しいです。

C#は volatile を言語に導入して、ダブルチェックロッキングを安全に使えるようです。
逆に言うと instance に volatile を使わない ダブルチェックロッキングは安全ではない可能性があります。

スタティックコンストラクタの実行中は、そのクラスは別スレッドからアクセスできないため、
囚人さんの指摘通り lockObj の volatile は不要ですね。

lockObj が null になる原因がわからないので lockObj を
private static readonly object lockObj = new Object();
と readonly にしてみたらどうでしょう。
lockObj に null を代入している場所がコンパイルエラーになります。
(きっと、こんな初歩的なミスではないと思いますが・・)

がんふぃーるど
ベテラン
会議室デビュー日: 2006/06/05
投稿数: 58
お住まい・勤務地: さいたま
投稿日時: 2006-08-30 04:12
>k_kazuさん
volatile付いてなかった…orz
MSのコードをコピったって書いてあったから完全に見過ごしてました…
(コードレビューア失格…)

ちょっと話題がそれますが、ダブルチェックロッキングをCLRが正しく
動くとMSが言い張ってるのは、volatileとして宣言した変数は最適化時に
順序が変わらないってことを実装しているってことなんですかね?
MSが正しく動くというのを信じて、bool型変数と特定のフィールドに
volatile宣言してたりします。
if(completeFlg == false) 〜 lock(lockObj) 〜 if(completeFlg == false)
で最後のif文内で複数のインスタンス(volatile宣言したやつ)を生成してから
completeFlgをtrueにしちゃってますね。
頭脳パン
ベテラン
会議室デビュー日: 2003/04/03
投稿数: 89
投稿日時: 2006-08-30 09:16
みなさん、たくさんのご意見ありがとうございます!

昨日、帰り道に本屋で.NETエキスパートっていう本を見たら
ちょうど、デザインパターンが載っていて、C#シングルトンは
volatileを使用すると書いてありました。

さっそく、instanceの方にvolatileをつけてみました。

が、結果は変わらずです。lockObjがnullで
ArgumentNullExceptionで"値を Null にすることはできません。"になります。

また、lockObjにreadonlyをつけても結果は同様でした。

スキルアップ/キャリアアップ(JOB@IT)