Локальная статическая инициализация без удержания блокировки позволяет избежать возможного тупика в C ++ 11? - PullRequest
4 голосов
/ 22 октября 2011

В статье http://www.open -std.org / jtc1 / sc22 / wg21 / docs /apers / 2008 / n2660.htm представлен алгоритм, который не требует блокировки во время инициализации локальная статическая переменная, но все же вызывает параллельный поток управления через определение переменной для ожидания завершения инициализации.

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

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

Может кто-нибудь привести пример, который демонстрирует, где возникает тупик, описанный выше?

Ответы [ 2 ]

4 голосов
/ 22 октября 2011

Если вы собираетесь удерживать блокировку во время локальной статической инициализации, возможны два варианта:

  1. Выделить мьютекс для каждой статики.
  2. Выделить только один мьютекс для всехstatics.

Я не уверен на 100%, но я полагаю, что цитата, на которую вы ссылаетесь, неявно предполагает дизайн 2. И действительно, алгоритм, представленный в статье, использует только один мьютекс для всей статики (называемыйmu в коде).

В схеме 2 вы получаете тупик, как описано ответом на переполнение стека .То есть, если вы на самом деле не держите мьютекс во время инициализации.Это достигается наличием флага трех состояний для каждой статики, который указывает одно из: не инициализировано, инициализируется, уже инициализировано.И используйте глобальный мьютекс для установки флага, но разблокируйте его во время инициализации.

3 голосов
/ 22 октября 2011

Это простое расширение классического тупика для случая, когда одна из блокировок предоставляется компилятором.

void A2B() { a.Lock(); B(); a.Unlock(); }
void B() { b.Lock(); ...; b.Unlock(); }
void B2A() { b.Lock(); A();  b.Unlock(); }
void A() { a.Lock(); ...; a.Unlock(); }

Классическая тупиковая ситуация возникает, если один поток вызывает A2B(), а другой поток вызывает B2A().

В статической блокировке инициализации компилятор обеспечивает блокировку b.

int A() { a.Lock(); ...; a.Unlock(); return 0; }
void B2A() { static int v = A(); }
void A2B() { a.Lock(); B2A(); a.Unlock(); }

Если вы предполагаете блокировку статической инициализации, тогда код тайно преобразуется в

void B2A() {
    if (!initialized) {
      b.Lock(); // standard double-check-lock
      if (!initialized) v = A();
      initialized=true;
      b.Unlock();
   }
}

Один поток вызывает A2B(), а другой - B2A().

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