2017年12月15日
川俣晶の縁側ソフトウェア技術雑記total 2441 count

LazyInitializer.EnsureInitializedメソッドで初期化しているのにコンストラクタが2回走る

Written By: 川俣 晶連絡先

問題 §

 マルチスレッド環境で、EnsureInitialized<T>(T) メソッドを使用して自作クラス型の変数を初期化しているが、希に自作クラスのコンストラクタが2回走ってしまうケースがある。

 LazyInitializer.EnsureInitializedメソッドはスレッドセーフであるというネットの情報は見ている。

原因 §

 EnsureInitialized<T>(T) メソッドは変数の書き換えに関してはスレッドセーフであるが、コンストラクタの実行に関してはスレッドセーフではない。

 もし、変数が未初期化であることを発見すると、新しいオブジェクトの作成を実行してしまう構造なので、【変数の値が常に健全である】ということは保証されるが、【コンストラクタが1回だけ実行される】ということは保証されない。

 (EnsureInitialized<T>(T) のソースコードを確認した結果、変数の書き換えはCompareExchangeで行うので、変数の変化はnullから有効な値への変化1回きりである。それ以後は書き込もうとしても書き込めない。つまり、2回目以降に実行されたコンストラクタが生成した値は破棄される)

解決 §

 もし、マルチスレッド環境下でコンストラクタが1回だけ実行されることを保証したいなら、EnsureInitialized<T>(T)ではなく、EnsureInitialized<T>(T, Boolean, Object) を使用する。2回コンストラクタを実行させないためのポイントは、以下の2つ。

  • 第2引数に対して全てのスレッドの呼び出しで【同じ】bool型変数を格納すること
  • 第3引数に対し、全てのスレッドの呼び出しで【同じ】同期オブジェクトを指定すること

 スレッド間で同じ変数を指定することは非常に重要。第2引数で指定する変数は必ずfalseで初期化しておく。第3引数の同期オブジェクトは変数の値がnullの場合は自動的に作成されるので初期化は重要ではない。

感想 §

「かなり時間を食ってしまったが、だいたい明らかにできた。ドキュメントがあまりに不十分で細部の動作をチェックするためにソースまで見る羽目になったのは非常によろしくないがな」

「ひぇ~。また.NET Frameworkのソース見たのかよ」

「おかげで、ほぼ確実と言えるレベルで解決策を説明できたけどな」

Facebook

キーワード【 川俣晶の縁側ソフトウェア技術雑記
【技術雑記】の次のコンテンツ
2018年
01月
25日
【Windows 10 バージョン 1709 機能更新プログラム】が失敗する問題
3days 0 count
total 2515 count
【技術雑記】の前のコンテンツ
2017年
12月
08日
C#で使用できるJSONシリアライザで配列を扱えるかを調べる
3days 0 count
total 4031 count

このコンテンツを書いた川俣 晶へメッセージを送る

[メッセージ送信フォームを利用する]

メッセージ送信フォームを利用することで、川俣 晶に対してメッセージを送ることができます。

この機能は、100%確実に川俣 晶へメッセージを伝達するものではなく、また、確実に川俣 晶よりの返事を得られるものではないことにご注意ください。

このコンテンツへトラックバックするためのURL

https://mag.autumn.org/tb.aspx/20171215150217
サイトの表紙【技術雑記】の表紙【技術雑記】のコンテンツ全リスト 【技術雑記】の入手全リスト 【技術雑記】のRSS1.0形式の情報このサイトの全キーワードリスト 印刷用ページ

管理者: 川俣 晶連絡先

Powered by MagSite2 Version 0.36 (Alpha-Test) Copyright (c) 2004-2021 Pie Dey.Co.,Ltd.