Синглтон и многопоточность - PullRequest
5 голосов
/ 18 июня 2010

У меня есть следующий класс

class Singleton
{
  private:

    static Singleton *p_inst;
    Singleton();

  public:

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

      return p_inst;
    }
};

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

Ответы [ 6 ]

7 голосов
/ 18 июня 2010

В многопоточности этот пункт

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, увидит только последнее назначение. Но с блокировкой вы можете предотвратить многократное назначение и подобные проблемы.

5 голосов
/ 18 июня 2010

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

4 голосов
/ 13 августа 2010

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

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

4 голосов
/ 18 июня 2010

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

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

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

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

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

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

1 голос
/ 18 июня 2010

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

  • Нужен ли вашему синглтону безопасность потоков?

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

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

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

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

  • Вас интересуют расходы на блокировку?

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

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

1 голос
/ 18 июня 2010

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

class Singleton
{
  private:

    Singleton();

  public:

    static Singleton * instance()
    {
      static Singleton inst;
      return &inst;
    }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...