Re[7]: Синглтон через умный указатель
От: sokel Россия  
Дата: 10.03.09 11:01
Оценка:
Здравствуйте, pegarus, Вы писали:

P>А всё-таки, что мешает использовать обычный мьютекс? Для защиты указателя и счётчика ссылок (ниже есть код). Никак не пойму, вижу только намёки... ???


Во первых, хотелось бы избежать использования мьютекса при каждом получении instance. Во вторых, не всегда можно статически инициализировать этот самый мьютекс, что опять таки возвращает нас к проблеме singleton-мьютекса.

Допустим, мы подобным образом, создали общий мьютекс для создания всех singleton объектов:

class single_lock
{
public:
    static mutex& instance(){ if(!lock) single_lock_init.initialize(); return *lock; }
private:
    single_lock() { if(!lock) initialize();  }
    void initialize() { lock = new dtl::mutex(); };
    static mutex* lock;
    static single_lock single_lock_init;
};
mutex* single_lock::lock = 0;
single_lock single_lock::single_lock_init;


Несостоятельность double check locking pattern обычно объясняют тем, что инструкции могут быть переупорядочены, то есть подобный код в общем случае не является потокобезопасным:

static T& instance()
{
    if(!obj)
    {
        scopelock lock(single_lock::instance());
        if(!obj) obj = new T();
    }
    return *obj;
}


Здесь вызов конструктора и присваивание указателя могут поменяться местами.

Интересно, а если создание объекта вынести во внешнюю функцию, запретив тем самым inlining конструктора объекта, не будет ли это являться универсальным memory barrier (если считать присваивание указателя атомарной операцией), например:

typedef void* (*fn_construct) ();

void* create_instance(fn_construct fn) { return fn(); } // выносим в .cpp

template<typename T>
class singleton
{
    static T* obj;
    static void* construct() { return new T(); }
    static void initialize()
    {
        scopelock lock(single_lock::instance());
        if(!obj) obj  = (T*) create_instance(construct);
    };
public:
    static T& instance()    
    { 
        if(!obj) initialize();
        return *obj;
    }
};

template<typename T> T* singleton<T>::obj = 0;


Тогда получим честную отложенную инициализацию... Но опять таки, что делать со временем жизни объекта — непонятно.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.