Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Singleton. Облом года.

351
15 января 2006 года
PitxBull
633 / / 22.12.2004
Я вот тут седня полдня девелопел Singleton устойчиво работающий в мультипоточной среде. Так вот оказывается его не существует. Даже для Windows. Получается Алекснадреску зря написал целую главу посвященную ему в своей книге. Бросаю вызов : пусть кто нить докажет что приведенный им код надежно работает в мультипоточной среде. Как известно это блокировка с двойной проверкой. Но она не работает.
351
15 января 2006 года
PitxBull
633 / / 22.12.2004
Код:
Button::Unpressed* Button::Unpressed::Instance()
{  
    static HANDLE hMutex = INVALID_HANDLE_VALUE;

    if ( hMutex == INVALID_HANDLE_VALUE ) hMutex = CreateMutex( NULL, TRUE, NULL );
   
    if ( !pInstance )
    {
        WaitForSingleObject( hMutex, INFINITY );
       
        if ( !pInstance ) pInstance = new Button::Unpressed();
    }

    return pInstance;
}


как следует из вышеприведенного кода существует опасность создания более чем одного объекта-мьютекса, причем хандл одного из них оказывается утерянным. Таким образом прибегая к блокировке при помощи объектов ядра мы всего лишь рекурсивно получаем ту же проблему : получение единственного объекта - теперь уже мьютекса. Вот в Java можно сделать целый метод синхронизированым объявив его synchronized. С точки зрения данного уровня абстракции проблема вроде решена. Вот только вопрос : как реализована в программной Java-машине инструкция synchronized под Windows?
3
15 января 2006 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by PitxBull
Я вот тут седня полдня девелопел Singleton устойчиво работающий в мультипоточной среде. Так вот оказывается его не существует. Даже для Windows. Получается Алекснадреску зря написал целую главу посвященную ему в своей книге. Бросаю вызов : пусть кто нить докажет что приведенный им код надежно работает в мультипоточной среде. Как известно это блокировка с двойной проверкой. Но она не работает.


Без паники!
Не следует делать глобальных категоричных выводов из своего неудачного эксперимента.
Не работает лишь твоя реализация.

Цитата:
Originally posted by PitxBull

Таким образом прибегая к блокировке при помощи объектов ядра мы всего лишь рекурсивно получаем ту же проблему : получение единственного объекта - теперь уже мьютекса.


ОС здесь не при чем, здесь проблема реализации.

Классический код для thread-safe singleton-а вышлядит так:

Код:
static T& getInstance()
{
  if (!_instance) {
    Locker lock(guard);
   
    if (!_instance)
      _instance = create();    
  }

  return *_instance;
}

Вся проблема проблема в том, что ты неверно реализовал залочку.
Создание guard в твоей реализации не атомарно. Кстати, Александреску делает акцент на атомарность.
Есть два решения:
1) создавать именованный объект синхронизации;
2) создавать объект синхронизации заранее и единым для класса синглетона, т.е. объявить статиком, но не для метода, а для класса.

Мне нравится второй вариант, а в качестве объекта лучше взять CS, а не Mutex. Бестрее CS работеает, чем Mutex.
351
16 января 2006 года
PitxBull
633 / / 22.12.2004
Тогда такой вариант :

Код:
class CriticalSection
{
public:
    CriticalSection()
    {
        InitializeCriticalSection( &cs );
    }

    ~CriticalSection()
    {
        DeleteCriticalSection( &cs );
    }

    Lock()
    {
        EnterCriticalSection( &cs );
    }

    Unlock()
    {
        LeaveCriticalSection( &cs );
    }

   
protected:

    CRITICAL_SECTION cs;
};

class Button
{
public:

    ............

    class Unpressed
    {
    public:

        Unpressed* Instance();

    protected:

        static CriticalSection cs;

      .........
     };

.....
};
     

CriticalSection Button::Unpressed::cs;

Button::Unpressed* Button::Unpressed::Instance()
{  
    if ( !pInstance )
    {
        cs.Lock();

    if ( !pInstance ) pInstance = new Button::Unpressed();

    cs.Unlock();
    }

    return pInstance;
}


Проблема вроде решена. Но как ? Если раньше я задавал вопрос как реализован метод synchronized в Java, то теперь его можно заменить на вопрос как реализован код
 
Код:
static CriticalSection cs;
?. Фактически проблема решена путем выноса процесса создания критической секции в момент выполнения программы до функции main. То есть когда в процессе гарантированно существует один поток и возможность выполнения несколькими потоками данного кода не существует. Но такой код тогда можно назвать только однопоточным. Таким образом, исходная задача : получение единственного объекта в мультипоточной среде не решена. А точнее она решена только относительно факта существования единственного обьекта, созданного в однопоточной среде.

Кстати интересно посмотреть реализацию
 
Код:
Locker lock(guard);
А также вопрос где можно скачать библиотеку Loki из книги Александреску. Приведеная в книге ссылка не работает.
2.4K
16 января 2006 года
dinasok51
219 / / 12.11.2005
Цитата:
Originally posted by PitxBull
Фактически проблема решена путем выноса процесса создания критической секции в момент выполнения программы до функции main. То есть когда в процессе гарантированно существует один поток и возможность выполнения несколькими потоками данного кода не существует. Но такой код тогда можно назвать только однопоточным. Таким образом, исходная задача : получение единственного объекта в мультипоточной среде не решена. А точнее она решена только относительно факта существования единственного обьекта, созданного в однопоточной среде.



Именованный Mutex гарантированно будет создан только в одном экземпляре, не зависимо от кол-ва потоков, процессов или процессоров.( если, конечно, имя везде одинаковое).

Код:
Button::Unpressed* Button::Unpressed::Instance()
{  
    static HANDLE hMutex = INVALID_HANDLE_VALUE;

    if ( hMutex == INVALID_HANDLE_VALUE )
        {
          hMutex = CreateMutex( "MyMutex", TRUE, NULL );
        }
   
    if ( !pInstance )
    {
       WaitForSingleObject( hMutex, INFINITY );
       if ( !pInstance )
           {
              pInstance = new Button::Unpressed();
           }
           ReleaseMutex( hMutex );         // Нужно освобождать
    }

    return pInstance;
}


А hMutex нужно будет освободить
3
16 января 2006 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by PitxBull
Фактически проблема решена путем выноса процесса создания критической секции в момент выполнения программы до функции main. То есть когда в процессе гарантированно существует один поток и возможность выполнения несколькими потоками данного кода не существует. Но такой код тогда можно назвать только однопоточным. Таким образом, исходная задача : получение единственного объекта в мультипоточной среде не решена. А точнее она решена только относительно факта существования единственного обьекта, созданного в однопоточной среде.


Без однопоточного кода, а точнее без атомарной операции, не обойтись, иначе никто не может гарантировать целостность транзакции. Это верное не только для компьютеров, но IMHO, для любых систем. На сколько мне известно, это одна из проблем на пути создания квантовых компьютеров.
Так что придется смириться... :)

Создание именованного объекта синхронизации - один из вариантов реализации гарантированного однопоточного выполнения, только в этом случае это задача ОС. Поэтому создание именованного мьютекса более трудоемкий процесс, чем создание и дальнейшее использование CS.

Цитата:
Originally posted by PitxBull

Кстати интересно посмотреть реализацию
 
Код:
Locker lock(guard);


Да там всё элементарно. В конструкторе Locker залочиваем guard, в деструкторе - разлочиваем.

Код:
class Locker
{
public:
    Locker(CriticalSection& cs) :_cs(cs) {
        cs.Lock();        
    }

    ~Locker() {
        cs.Unlock();
    }

private:
    CriticalSection& _cs;
};


Цитата:
Originally posted by PitxBull

А также вопрос где можно скачать библиотеку Loki из книги Александреску. Приведеная в книге ссылка не работает.


Думаю, здесь google поможет.
Но применять на практике не советую... Лучше уж boost.

351
17 января 2006 года
PitxBull
633 / / 22.12.2004
Пока остановился на таком варианте.

Код:
#pragma once
#include "CriticalSection.h"

template<class T> class Singleton
{
public:
   
    static T* Instance()
    {
        if ( !_Instance )
        {
            _CriticalSection.Lock();

            if ( !_Instance ) _Instance = new T;

            _CriticalSection.Unlock();
        }

        return _Instance;
    }

protected:

    static T* _Instance;
    static CriticalSection _CriticalSection;
};

template<class T> T* Singleton<T>::_Instance = 0;
template<class T> CriticalSection Singleton<T>::_CriticalSection;
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог