これは、二重チェック ロックに関するこの投稿のフォローアップです。「古い」投稿にフォローアップを投稿しても、新しい投稿を送信するほど表示/アクティブにならないように思われるため、新しい投稿を書いています。おそらく、ほとんどの人がスタックオーバーフローの投稿をアクティビティのレベルで並べ替えていないためです。 .
回答者の皆様、元の投稿へのご意見ありがとうございます。Joe Duffy の優れた本「Concurrent Programming on Windows」を参照した後、以下のコードを使用する必要があると考えています。一部の変数の名前変更と InterlockedXXX 行を除いて、これは彼の本のコードとほとんど同じです。次の実装では以下を使用します。
- 一時ポインターと「実際の」ポインターの両方にvolatileキーワードを追加して、コンパイラーからの並べ替えから保護します。
- CPUからの並べ替えから保護するためのInterlockedCompareExchangePointer。
だから、それはかなり安全なはずです(...そうですか?):
template <typename T>
class LazyInit {
public:
typedef T* (*Factory)();
LazyInit(Factory f = 0)
: factory_(f)
, singleton_(0)
{
::InitializeCriticalSection(&cs_);
}
T& get() {
if (!singleton_) {
::EnterCriticalSection(&cs_);
if (!singleton_) {
T* volatile p = factory_();
// Joe uses _WriterBarrier(); then singleton_ = p;
// But I thought better to make singleton_ = p atomic (as I understand,
// on Windows, pointer assignments are atomic ONLY if they are aligned)
// In addition, the MSDN docs say that InterlockedCompareExchangePointer
// sets up a full memory barrier.
::InterlockedCompareExchangePointer((PVOID volatile*)&singleton_, p, 0);
}
::LeaveCriticalSection(&cs_);
}
#if PREVENT_IA64_FROM_REORDERING
_ReadBarrier();
#endif
return *singleton_;
}
virtual ~LazyInit() {
::DeleteCriticalSection(&cs_);
}
private:
CRITICAL_SECTION cs_;
Factory factory_;
T* volatile singleton_;
};