2010-06-18 7 views
5

У меня есть следующий классSingleton & Многопоточность

class Singleton 
{ 
    private: 

    static Singleton *p_inst; 
    Singleton(); 

    public: 

    static Singleton * instance() 
    { 
     if (!p_inst) 
     { 
     p_inst = new Singleton(); 
     } 

     return p_inst; 
    } 
}; 

Пожалуйста, не конкретизирующих меры предосторожности при реализации Singleton в многопоточной среде.

+0

при вставке кода убедитесь, что вы используете пробелы, а не вкладки, так как последний придает отметку уценке. – ChrisF

+2

Проверьте здесь: http://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289 –

+4

Меры предосторожности, которые я бы взял, это не реализовать одноэлементный режим. У них всегда больше проблем, чем их стоит. –

ответ

7

В многопоточность, что пункт

if(!p_inst) 
{ 
    p_inst = new Singleton(); 
} 

на самом деле 3 отдельные действия. Вы получаете значение p_inst, устанавливая значение p_inst и записывая значение p_inst. Таким образом, get-set-write означает, что вам нужно поместить блокировку вокруг p_inst, иначе вы можете иметь 2 потока, которые создают значение Singleton, которое использует каждый поток.

Вот как вы можете просмотреть этот вопрос, предположит, что ваш Singleton имеет изменяемое поле val:

thread A -> p_inst is NULL 
    thread B -> p_inst is NULL 
     thread A -> set to Singleton (1) 
      thread B -> set to Singleton (2) 
       thread C -> p_inst is Singleton (2) 
        thread A -> set val to 4 
         thread B -> set val to 6 
         thread C -> get val (it's 6) 
          thread A -> get val (it's 4!!) 

Вы видите? Там есть 2 копии Синглтона, о которых никто не знает. Третий поток, который проверяет на Singleton, увидит только последнее задание. Но с блокировкой вы можете предотвратить множественное назначение и эти проблемы.

+0

Это что-то вроде? статический Singleton * экземпляр() { if (! P_inst) { Mutex m; m.lock(); // Критический раздел if (! P_inst) p_inst = new Singleton(); // Конец критического раздела m.unlock(); } return p_inst; } – ronan

+0

Я понимаю, что с помощью usute mutex и lock это становится медленным проектом, но тогда есть ли способ его оптимизировать? – ronan

+3

@ronan: Да, не используйте этот анти-шаблон, который является «singleton». – ereOn

5

Вам нужно будет использовать мьютексы и заблокировать указатель перед назначением или чтением, делая этот медленный (и imo просто страшный) шаблон дизайна.

+0

+1: Мои мысли, точно. – ereOn

1

Для многопоточной конструкции используйте статическую переменную в функции instance(). Инициализация статических переменных автоматически защищается компилятором. Любые другие операции требуют явной блокировки. Использовать мьютексы.

class Singleton 
{ 
    private: 

    Singleton(); 

    public: 

    static Singleton * instance() 
    { 
     static Singleton inst; 
     return &inst; 
    } 
}; 
+0

Я должен понять, могу ли я использовать ключевое слово volatile? Как это помогает? – ronan

+4

Нет гарантии от стандарта pre-C++ 0x, что инициализация статических переменных в методе защищена от условий гонки. Некоторые компиляторы C++ могут сделать это безопасным, но многие этого не делают. – rjnilsson

+0

Это НЕПРЕРЫВНЫЙ pre-C++ 0x. Другой комментатор уже ссылался на это, но я буду откровенен в этом. – mike

1

Вы должны спросить себя, что вы подразумеваете под потоковой безопасностью.

  • Нужен ли вам один синглтон для защиты резьбы?

    Если нет, то рассмотреть нить статический подход

  • Вы хотите, чтобы гарантировать, что никакие два экземпляра одноплодной никогда не получить создали?

    Если нет, описанное выше решение, вероятно, хорошо, без каких-либо блокировки: у вас есть состояние гонки на строительство - но вы не волнует поскольку в конечном счете, только один выживет - тем не менее, вы можете иметь утечка ресурсов, если вы не будете осторожны, что может быть или не быть значительным. (Это по существу кеш).

  • Вы хотите гарантировать, что в конечном итоге останется только один экземпляр?

  • Вы заботитесь о стоимости запирания?

    Если нет (что довольно распространено), вы можете просто поместить замок вокруг него и быть счастливым.

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

+0

- Спасибо за объяснение. Мне нужно будет получить известную статическую тему, в основном я хочу, чтобы Singleton был потокобезопасным. – ronan

4

Я буду краток: это зависит от вашего компилятора.

  • Если ваш компилятор реализует многопоточную синхронизацию для локальных статических (то есть статических экземпляров, встроенных в метод), затем используйте его и выполняйте с ним.
  • Если нет, Херб Саттер доказал, что это невозможно.

Теперь вы должны понимать, что вам может не понадобиться это.

Есть два способа борьбы с этим, которые не требуют многопоточного понимания.

  1. Просто используйте экземпляр static вместо того, чтобы динамически его распределять. Безопасный и простой. Может возникнуть проблема с порядком инициализации, если вам нужно получить к ней доступ от другой переменной static
  2. Создайте экземпляр singleton ПЕРЕД наличием нескольких потоков. Обычный трюк - назвать его main.

Конечно, реальный вопрос: не можете ли вы просто передать ссылку на объект, а не создавать глобальную переменную? Это облегчило бы тестирование;)

4

Вы можете устранить все проблемы, просто выделив (любым способом) такие объекты перед запуском нескольких потоков. Это может быть не всегда возможно из-за конструктивных ограничений (с использованием синглтонов в статике, вам НУЖНО ленивое распределение и т. Д.), Но это просто и дает вам контроль над последовательностью создания. Иногда отслеживание проблем в отношении порядка и времени размещения таких объектов - это проблема, которую вы легко можете избежать.

P.S. - Я знаю, что это прямо не отвечает на ваш вопрос, но это может быть практическое решение реальной проблемы без сложности.

Смежные вопросы