О двойной проверке блокировки:
class Foo
{
Resource * resource;
Foo() : resource(nullptr) { }
public:
Resource & GetResource()
{
if(resource == nullptr)
{
scoped_lock lock(mutex);
if(resource == nullptr)
resource = new Resource();
}
return *resource;
}
}
Это не поточно-ориентированный поток, так как вы проверяете, является ли адрес ресурса нулевым.Поскольку существует вероятность того, что указатель ресурса будет назначен ненулевому значению непосредственно перед инициализацией объекта Resource, на который он указывает.
Но с функцией «атомарности» C ++ 11 у вас может быть вдвойнепроверенный механизм блокировки.
class Foo
{
Resource * resource;
std::atomic<bool> isResourceNull;
public:
Foo() : resource(nullptr), isResourceNull(true) { }
Resource & GetResource()
{
if(isResourceNull.load())
{
scoped_lock lock(mutex);
if(isResourceNull.load())
{
resource = new Resoruce();
isResourceNull.store(false);
}
}
return *resource;
}
}
РЕДАКТИРОВАТЬ: без атомарности
#include <winnt.h>
class Foo
{
volatile Resource * resource;
Foo() : resource(nullptr) { }
public:
Resource & GetResource()
{
if(resource == nullptr)
{
scoped_lock lock(mutex);
if(resource == nullptr)
{
Resource * dummy = new Resource();
MemoryBarrier(); // To keep the code order
resource = dummy; // pointer assignment
}
}
return *const_cast<Resource*>(resource);
}
}
MemoryBarrier()
гарантирует, что dummy
будет сначала создано, а затем присвоено resource
.Согласно эта ссылка назначения указателей будет атомарной в системах x86 и x64.И volatile
гарантирует, что значение resource
не будет кэшировано.