Может ли произойти копирование через операторы синхронизации с? - PullRequest
13 голосов
/ 19 января 2012

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

user_type foo()
{
  unique_lock lock( global_mutex );
  return user_type(...);
}

user_type result = foo();

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

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


Чтобы убедиться, что ответ не относится только к особому случаю, обратите внимание, что разрешение на копирование (согласно моим прочтениям) все еще разрешено, если у меня есть утверждение типа new (&result)( foo() ). То есть result не обязательно должен быть стековым объектом. user_type сам может также работать с данными, разделяемыми между потоками.


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

Ответы [ 2 ]

5 голосов
/ 19 января 2012

Потоки не имеют к этому никакого отношения, но порядок конструкторов / деструкторов блокировки может повлиять на вас.

Глядя на низкоуровневые шаги, которые ваш код выполняет без исключения копирования, один за другим (используя опцию GCC -fno-elide-constructors):

  1. Construct lock.
  2. Построить временный user_type с (...) аргументами.
  3. Скопируйте и создайте временное возвращаемое значение функции типа user_type, используя значение из шага 2.
  4. Удалите временное значение из шага 2.
  5. Уничтожьте lock.
  6. Скопируйте конструкцию user_type result, используя значение из шага 3.
  7. Уничтожьте временное из шага 3.
  8. Позже уничтожьте result.

Естественно, что при оптимизации нескольких копий это будет просто:

  1. Построить lock.
  2. Построить объект result напрямую с помощью (...).
  3. Уничтожить lock.
  4. Позже уничтожить result.

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

Afterfterts :

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

Естественно, этоозначает, что ваш объект изначально плохо спроектирован, поскольку копии не ведут себя как исходный объект.

Ссылка :

В черновике C ++ 1112.8.31 (похожая формулировка без всех «ходов» есть в C ++ 98:

При выполнении определенных критериев реализация может опустить конструкцию копирования / перемещения объекта класса, даже если конструктор копирования / перемещения и / или деструктор для объекта имеют побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной операции копирования / перемещения как просто два разных способа обращения к одному и тому же объекту, иуничтожение этого объекта происходит в более поздние времена, когда два объекта были бы уничтожены без оптимизации.Использование операций копирования / перемещения, называемых копированием, разрешено в следующих обстоятельствах (которые могут быть объединены для удаления нескольких копий):

  • в операторе возврата в функции с классомтип возвращаемого значения, когда выражение является именем энергонезависимого автоматического объекта (отличного от параметра функции или оператора catch) с тем же типом cvunqualified, что и тип возвращаемого значения функции, операцию копирования / перемещения можно опустить, создав автоматическийобъект непосредственно в возвращаемое значение функции

  • (функция или параметр catch-условия), область действия которого не выходит за пределы самого внутреннего включающего блока try (если он есть),Операция копирования / перемещения из операнда в объект исключения может быть опущена путем создания автоматического объекта непосредственно в объект исключения

  • , когда временный объект класса, который не был связан со ссылкой, будетбыть скопирован / перемещен в объект класса с тем же cv-unквалифицированный тип, операция копирования / перемещения может быть опущена путем создания временного объекта непосредственно в цель пропущенного копирования / перемещения

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

Пункты 1 и 3 в вашем примере сотрудничают, чтобы исключить все копии.

0 голосов
/ 21 января 2012

"Чтобы убедиться, что ответ не относится только к особому случаю, обратите внимание, что разрешение на копирование (согласно моим прочтениям) все еще разрешено, если у меня есть утверждение типа new (&result)( foo() ). То есть результат не требуется быть стековым объектом. user_type сам может также работать с данными, разделяемыми между потоками. "

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

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