- - PR -
マルチスレッドシングルトンでロックオブジェクトがnull
投稿者 | 投稿内容 | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 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; } } } | ||||||||
|
投稿日時: 2006-08-29 21:50
再現するコードはありますか?
_________________ 囚人のジレンマな日々 | ||||||||
|
投稿日時: 2006-08-29 21:54
volatile 宣言が抜けているのが原因だと思うのですが・・。 同ページに 『変数が volatile として宣言されていることで、インスタンス変数への代入が完了するまでは、インスタンス変数にアクセスできなくなっています。』 と書かれています。 あれ? マイクロソフトのページでは lockObj に volatile 宣言がありませんね? これが原因で lockObj が NULL になっているのかもしれません。 instance と lockObj 両方に volatile 宣言を追加して検証して その結果を教えてください。 (できればマルチCPUの環境でテストできるとベスト・・) | ||||||||
|
投稿日時: 2006-08-29 22:08
ん〜、lockObj には、volatile は必要なさそうなんですけどね。
でも確かに instance には必要かも。というか処理する順序が悪いのかな。 NullReferenceException をスローしてるのは、instance 変数を使ってるどこかかな?
を
こうしたら volatile は必要なさそうですが、毎度 lock するのもウザイですね。 _________________ 囚人@わんくま同盟 囚人のジレンマな日々 [ メッセージ編集済み 編集者: 囚人 編集日時 2006-08-29 22:10 ] | ||||||||
|
投稿日時: 2006-08-30 00:29
最初のコードが一番正しいと思いますよ。
if(instance == null) 〜 lock(lockObj) 〜 if(instance == null) は有名なイディオムです。(ダブルチェックロッキングだったかな…) ソースを見る限りinstanceがnullになる原因が見当たらないので、 現象が発生するDLLやEXEに対しILDASMやReflectorを使用し、 問題となるクラスのタイプイニシャライザ(.cctor)にlockObjを 初期化(インスタンス化)するコードがあるかチェックしてみるとか… (Staticなフィールドの初期化は、タイプイニシャライザの先頭で 行われているはずです。) | ||||||||
|
投稿日時: 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-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にしちゃってますね。 | ||||||||
|
投稿日時: 2006-08-30 09:16
みなさん、たくさんのご意見ありがとうございます!
昨日、帰り道に本屋で.NETエキスパートっていう本を見たら ちょうど、デザインパターンが載っていて、C#シングルトンは volatileを使用すると書いてありました。 さっそく、instanceの方にvolatileをつけてみました。 が、結果は変わらずです。lockObjがnullで ArgumentNullExceptionで"値を Null にすることはできません。"になります。 また、lockObjにreadonlyをつけても結果は同様でした。 |