Подтвердить что ты не робот

Потоковые безопасные статические переменные без мьютексинга?

Помню, что статические переменные, объявленные внутри методов, не являются потокобезопасными. (См. Что о синглете Meyer?, как упоминалось Тодд Гарднер)

Dog* MyClass::BadMethod()
{
  static Dog dog("Lassie");
  return &dog;
}

Моя библиотека генерирует код С++ для компиляции конечных пользователей как часть их приложения. Созданный им код должен инициализировать статические переменные в поточно-безопасном межплатформенном режиме. Я хотел бы использовать boost::call_once для mutex инициализацию переменных, но затем конечные пользователи подвергаются зависимости Boost.

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

4b9b3361

Ответ 1

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

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

Здесь несколько вариантов, если безопасность потока является обязательной:

  • Не ленитесь (загрузитесь): Инициализируйте во время статической инициализации. Это может быть проблемой, если другой статик вызывает эту функцию в конструкторе, так как порядок статической инициализации undefined (см. здесь).
  • Используйте boost (как вы сказали) или Loki
  • Сверните собственный синглтон на поддерживаемых платформах (вероятно, следует избегать, если только вы эксперт по потоку)
  • Блокировка мьютекса каждый раз, когда вам нужен доступ. Это может быть очень медленно.

Пример для 1:

// in a cpp:
namespace {
    Dog dog("Lassie");
}

Dog* MyClass::BadMethod()
{
  return &dog;
}

Пример для 4:

Dog* MyClass::BadMethod()
{
  static scoped_ptr<Dog> pdog;
  {
     Lock l(Mutex);
     if(!pdog.get())
       pdog.reset(new Dog("Lassie"));
  }
  return pdog.get();
}

Ответ 2

Не уверен, означает ли это то, что вы имеете в виду или нет, но вы можете удалить зависимость повышения в системах POSIX, вызывая вместо этого pthread_once. Думаю, вам придется делать что-то другое в Windows, но, избегая этого, именно поэтому boost имеет библиотеку потоков в первую очередь и почему люди платят цену в зависимости от этого.

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

Ответ 3

Один из способов сделать это, который не требует мьютекса для обеспечения безопасности потоков, заключается в том, чтобы сделать singleton файлом статическим, а не функцией static:

static Dog dog("Lassie");
Dog* MyClass::BadMethod()
{
  return &dog;
}

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

Ответ 4

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

Это может быть так просто, как просто документировать, что они должны вызвать функцию MyInit() в основном потоке, прежде чем делать что-либо еще. Затем вы создаете MyInit() для создания и уничтожения одного объекта каждого типа, который содержит одну из этих статики.

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

Если они не соответствуют правилам, изложенным в вашей документации, все ставки отключены. Правило, которое они должны назвать функцией инициализации или зависит от Boost, для меня не является необоснованным.

Ответ 5

AFAIK, единственный раз, когда это было сделано безопасно и без мьютексов или предшествующей инициализации глобальных экземпляров, находится в Matthew Wilson Imperfect С++, который обсуждает, как это сделать, используя "мьютез спина". Я не рядом с моей копией, поэтому не могу сказать вам точнее в это время.

IIRC, есть несколько примеров использования этого в библиотеках STLSoft, хотя я не могу вспомнить, какие компоненты в на этот раз.