4 General principles [intro]

4.7 Multi-threaded executions and data races [intro.multithread]

Athread of execution (также известный как athread) - это единый поток управления в программе, включая начальный вызов конкретной функции верхнего уровня и рекурсивно включающий каждый вызов функции, впоследствии выполняемый потоком. [ Note: Когда один поток создает другой, первоначальный вызов функции верхнего уровня нового потока выполняется новым потоком, а не создающим потоком. ] Каждый поток в программе потенциально может получить доступ к каждому объекту и функции в программе. При размещенной реализации программа на C ++ может иметь более одного потока, выполняемого одновременно. Выполнение каждого потока продолжается, как определено в остальной части настоящего международного стандарта. Выполнение всей программы состоит из выполнения всех ее потоков. [ Обычно выполнение можно рассматривать как чередование всех его потоков. Однако некоторые виды атомарных операций, например, допускают выполнение, несовместимое с простым чередованием, как описано ниже. ] В автономной реализации определяется, может ли программа иметь более одного потока выполнения.end note11 Note: end note

Для обработчика сигнала, который не выполняется в результате вызова std​::​raise функции, не указано, какой поток выполнения содержит вызов обработчика сигнала.

Объект с автоматическим или потокомstorage duration связан с одним конкретным потоком, и к нему может получить доступ другой поток только косвенно черезpointer или reference.

4.7.1 Data races [intro.races]

Значение объекта, видимого потокуT в определенной точке, - это начальное значение объекта, значение, присвоенноеTобъекту другим потоком, или значение, присвоенное объекту другим потоком, в соответствии с приведенными ниже правилами. [ Note: В некоторых случаях вместо этого может быть неопределенное поведение. Большая часть этого раздела мотивирована желанием поддерживать атомарные операции с явными и подробными ограничениями видимости. Однако он также неявно поддерживает более простой вид для более ограниченных программ. ]end note

Два вычисления выражения,conflict если одно из них изменяет a, memory location а другое читает или изменяет одну и ту же ячейку памяти.

Библиотека определяет рядatomic operations операцийmutexes , которые специально определены как операции синхронизации. Эти операции играют особую роль в том, чтобы сделать назначения в одном потоке видимыми для другого. Операция синхронизации в одной или нескольких ячейках памяти является либо операцией потребления, либо операцией получения, либо операцией освобождения, либо одновременно операцией получения и освобождения. Операция синхронизации без связанной ячейки памяти является ограничением и может быть либо ограничением получения, либо ограничением разблокировки, либо одновременно ограничением захвата и разблокировки. Кроме того, существуют упрощенные атомарные операции, которые не являются операциями синхронизации, и атомарные операции чтения-изменения-записи, которые имеют особые характеристики. [ Note: Например, вызов, который получает мьютекс, будет выполнять операцию получения в местах, составляющих мьютекс. Соответственно, вызов, освобождающий тот же мьютекс, выполнит операцию освобождения в тех же местах. Неформально выполнение операции освобождения A заставляет предыдущие побочные эффекты в других ячейках памяти становиться видимыми для других потоков, которые позже выполняют операцию потребления или получения A. «Расслабленные» атомарные операции не являются операциями синхронизации, хотя, как и операции синхронизации, они не могут участвовать в гонке данных. ]end note

Все модификации конкретного атомарного объектаM происходят в определенном общем порядке, называемомmodification order ofM. [ Note: Для каждого атомарного объекта существует отдельный заказ. Не требуется, чтобы их можно было объединить в единый общий заказ для всех объектов. В общем, это будет невозможно, поскольку разные потоки могут наблюдать модификации разных объектов в несовместимом порядке. ]end note

release sequence Во главе с операцией освобожденияA на атомном объекте M является максимальной смежным субом-последовательность побочных эффектов в порядке модификацииM, где первая операция являетсяA, и каждой последующей операция

  • выполняется тем же потоком, который выполнялA, или

  • это атомарная операция чтения-изменения-записи.

Определенная библиотека вызываетsynchronize with другие вызовы библиотеки, выполняемые другим потоком. Например, атомарное освобождение хранилища синхронизируется с получением загрузки, которое берет свое значение из store ([atomics.order]). [ Note: За исключением указанных случаев, чтение более позднего значения не обязательно обеспечивает видимость, как описано ниже. Такое требование иногда мешает эффективной реализации. ] [ Спецификации операций синхронизации определяют, когда один читает значение, записанное другим. Для атомарных объектов определение ясное. Все операции с данным мьютексом выполняются в едином общем порядке. Каждое получение мьютекса «считывает значение, записанное» последним выпуском мьютекса. ]end noteNote: end note

От оценкиAcarries a dependency к оценке,B если

  • значениеA используется как операндB, если:

    или

  • A записывает скалярный объект или битовое полеM,B читает значение, записанноеA изM, иA упорядочивается доB, или

  • для некоторой оценкиX,A несет в себе зависимость кX, и X несет в себе зависимость кB.

[ Note: «Несет зависимость от» - это подмножество «упорядочено до» и аналогично строго внутри потока. ]end note

ОценкаA -dependency-ordered before это оценка, B если

  • A выполняет операцию выпуска над атомарным объектомM, а в другом потокеB выполняет операцию потребленияM и считывает значение, записанное любым побочным эффектом в последовательности выпуска, озаглавленнойA, или

  • для некоторой оценкиX,A упорядочивается по зависимостям раньшеX и X несет в себе зависимость отB.

[ Note: Отношение «предварительно упорядочено зависимостями» аналогично «синхронизируется с», но использует освобождение / потребление вместо выпуска / получения. ]end note

ОценкаAinter-thread happens before оценка,B если

  • A синхронизируется сB, или

  • A упорядочивается по зависимости раньшеB, или

  • для некоторой оценкиX

    • A синхронизируется с предыдущимX иX упорядочиваетсяB, или

    • A упорядочивается до,X аX межпоточное происходит раньшеB, или

    • A inter-thread происходит раньше,X аX inter-thread - раньшеB.

[ Note: Отношение «между потоками происходит до» описывает произвольные конкатенации отношений «упорядочено до», «синхронизируется с» и «упорядочено по зависимости до» с двумя исключениями. Первое исключение состоит в том, что конкатенация не может заканчиваться словами «в порядке зависимости до», за которым следует «упорядочено до». Причина этого ограничения заключается в том, что операция потребления, участвующая в соотношении «зависимость упорядочена до», обеспечивает упорядочение только в отношении операций, от которых эта операция потребления фактически несет зависимость. Причина, по которой это ограничение применяется только к концу такой конкатенации, состоит в том, что любая последующая операция освобождения будет обеспечивать необходимое упорядочение для предыдущей операции потребления. Второе исключение состоит в том, что конкатенация не может состоять полностью из «предшествующей последовательности». Причины этого ограничения: (1) разрешить транзитивное закрытие «межпотока происходит до» и (2) отношение «происходит до», определенное ниже, предусматривает отношения, полностью состоящие из «упорядочено до». ]end note

ОценкаAhappens before оценкаB (или, что то же самое,Bhappens afterA), если:

  • A стоит раньшеB, или

  • A межпоток происходит раньшеB.

Реализация должна гарантировать, что выполнение программы не демонстрирует цикл в отношении «произошло до». [ Note: В противном случае этот цикл был бы возможен только при использовании операций потребления. ]end note

ОценкаAstrongly happens before оценка,B если

  • A стоит раньшеB, или

  • A синхронизируется сB, или

  • A сильно бывает доX иX сильно бывает доB.

[ Note: В отсутствие операций потребления, то происходит раньше и строго происходит до того, как отношения идентичны. Сильно бывает до того, как существенно исключает операции потребления. ]end note

visible side effectA На объект скалярного или битового поляM относительно вычисления значенияB изM удовлетворяет условиям:

  • A происходит раньшеB и

  • нет никакой другой побочный эффект ,X чтобыM таким образом, чтоA происходит доX иX происходит доB.

Значение неатомарного скалярного объекта или битового поляM, определяемое оценкойB, должно быть значением, сохраненным видимым побочным эффектом A. [ Note: Если есть неопределенность в отношении того, какой побочный эффект для неатомарного объекта или битового поля является видимым, то поведение либо не определено, либо не определено. ] [ Это означает, что операции с обычными объектами не меняются явно. На самом деле это невозможно обнаружить без гонок данных, но необходимо гарантировать, что гонки данных, как определено ниже, и с соответствующими ограничениями на использование атомики, соответствуют гонкам данных в простом чередующемся (последовательно согласованном) выполнении. ]end noteNote: end note

Значение атомарного объектаM, определяемое оценкойB, должно быть значением, сохраненным некоторым побочным эффектом,A который модифицируетсяM, чегоB не было раньшеA. [ Note: Набор таких побочных эффектов также ограничен остальными правилами, описанными здесь, и, в частности, требованиями согласованности ниже. ]end note

Если операцияA , изменяющая атомарный объект,M происходит до операции,B которая изменяетM, то онаA должна быть раньше, чемB в порядке модификацииM. [ Note: Это требование известно как согласованность записи-записи. ]end note

Если значение вычислениеA атомного объектаM происходит до вычисления значенияB изM, иA принимает значение от побочного эффектаX наM, то значение вычисляется путемB должно быть либо значение хранитсяX или значение , сохраненное с помощью побочного эффектаY на M, гдеY следуетX в порядок модификацииM. [ Note: Это требование известно как согласованность чтения-чтения. ]end note

Если вычислениеA значения атомарного объектаM происходит до операции,B которая модифицируетM, тогдаA оно должно принимать свое значение из побочного эффектаX наM, гдеX предшествуетB в порядке модификацииM. [ Note: Это требование известно как согласованность чтения-записи. ]end note

Если побочный эффектX на атомном объектеM происходит до вычисления значенияB изM, то оценкаB принимает свое значение отX или от побочного эффекта ,Y который следуетX в порядке модификацииM. [ Note: Это требование известно как согласованность записи-чтения. ]end note

[ Note: Четыре предшествующих требования согласованности эффективно запрещают компилятор переупорядочивать атомарные операции для одного объекта, даже если обе операции являются ослабленными нагрузками. Это фактически обеспечивает гарантию согласованности кэша, предоставляемую большинством оборудования, доступного для атомарных операций C ++. ]end note

[ Note: Значение, наблюдаемое при загрузке атома, зависит от отношения «произошло раньше», которое зависит от значений, наблюдаемых при загрузке атома. Предполагаемое прочтение состоит в том, что должна существовать ассоциация атомарных нагрузок с модификациями, которые они наблюдают, что вместе с соответствующим образом выбранными порядками модификации и отношением «происходит до», полученным, как описано выше, удовлетворяют результирующим ограничениям, как наложено здесь. ]end note

Два действия -potentially concurrent если

  • они выполняются разными потоками, или

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

Выполнение программы содержит,data race если она содержит два потенциально одновременных конфликтующих действия, по крайней мере одно из которых не является атомарным, и ни одно из них не происходит раньше другого, за исключением особого случая для обработчиков сигналов, описанного ниже. Любая такая гонка данных приводит к неопределенному поведению. [ Note: Можно показать, что программы, которые правильно используют мьютексы иmemory_­order_­seq_­cst операции для предотвращения всех гонок данных и не используют никаких других операций синхронизации, ведут себя так, как если бы операции, выполняемые составляющими их потоками, просто чередовались, при этом каждое вычисление значения объекта берется из последнего побочный эффект на этот объект в этом чередовании. Обычно это называют «последовательной согласованностью». Однако это относится только к программам без гонки данных, а программы без гонки данных не могут наблюдать большинство программных преобразований, которые не изменяют семантику однопоточной программы. Фактически, большинство однопоточных программных преобразований по-прежнему разрешены, поскольку любая программа, которая в результате ведет себя иначе, должна выполнять неопределенную операцию. ]end note

Два доступа к одному и тому же объекту типаvolatile std​::​sig_­atomic_­t не приводят к гонке данных, если оба происходят в одном потоке, даже если один или несколько происходит в обработчике сигнала. Для каждого вызова обработчика сигнала оценки, выполняемые потоком, вызывающим обработчик сигнала, могут быть разделены на две группы,A иB, таким образом, никакие оценки не B происходят до оценок вA, и оценки такихvolatile std​::​sig_­atomic_­t объектов принимают значения, как если бы все оценки вA произошли до выполнения. обработчика сигнала и выполнение обработчика сигнала произошло до всех вычислений вB.

[ Note: Преобразования компилятора, которые вводят назначения для потенциально разделяемой области памяти, которая не может быть изменена абстрактной машиной, как правило, запрещены настоящим международным стандартом, поскольку такое назначение может перезаписать другое назначение другим потоком в случаях, когда выполнение абстрактной машины не сталкивались с гонкой данных. Это включает в себя реализации назначения элементов данных, которые перезаписывают соседние элементы в отдельных ячейках памяти. Изменение порядка атомных нагрузок в случаях, когда рассматриваемые атомы могут быть псевдонимами, также обычно не допускается, поскольку это может нарушать правила согласованности. ]end note

[ Note: Преобразования, которые вводят спекулятивное чтение потенциально разделяемой области памяти, могут не сохранять семантику программы C ++, как определено в этом международном стандарте, поскольку они потенциально вызывают гонку данных. Однако они обычно действительны в контексте оптимизирующего компилятора, который нацелен на конкретную машину с четко определенной семантикой для гонок данных. Они были бы недействительны для гипотетической машины, которая не терпит гонок или обеспечивает обнаружение гонок на оборудовании. ]end note

4.7.2 Forward progress [intro.progress]

Реализация может предполагать, что любой поток в конечном итоге выполнит одно из следующих действий:

  • прекратить,

  • выполнить вызов функции ввода-вывода библиотеки,

  • выполнить доступ через изменчивое значение glvalue, или

  • выполнить операцию синхронизации или атомарную операцию.

[ Note: Это сделано для того, чтобы позволить преобразования компилятора, такие как удаление пустых циклов, даже если завершение не может быть доказано. ]end note

Выполнение элементарных функций, которые либо определены как есть,lock-free либо указаны какlock-free естьlock-free executions.

  • Если есть только один поток, которого нетblocked в стандартной библиотечной функции, выполнение в этом потоке без блокировки должно завершиться. [ Note: Одновременное выполнение потоков может помешать продвижению безблокирующего выполнения. Например, такая ситуация может возникнуть с реализациями условного хранилища с блокировкой загрузки. Это свойство иногда называют свободным от препятствий. ] end note

  • Когда одно или несколько запусков без блокировки выполняются одновременно, по крайней мере одно должно завершиться. [ Note: Для некоторых реализаций трудно обеспечить абсолютные гарантии этого эффекта, поскольку повторяющиеся и, в частности, неподходящие помехи со стороны других потоков могут препятствовать продвижению вперед, например, путем многократного перехвата строки кэша для несвязанных целей между инструкциями с блокировкой загрузки и условными сохранениями. Реализации должны гарантировать, что такие эффекты не могут на неопределенное время задерживать прогресс в ожидаемых рабочих условиях, и что такие аномалии, следовательно, могут безопасно игнорироваться программистами. Вне этого документа это свойство иногда называют свободным от блокировки. ] end note

Во время выполнения потока выполнения каждое из следующего называетсяexecution step:

  • завершение потока исполнения,

  • выполнение доступа через изменчивое значение glvalue, или

  • завершение вызова функции ввода-вывода библиотеки, операции синхронизации или атомарной операции.

Вызов стандартной библиотечной функции, которая,blocks как считается, непрерывно выполняет шаги выполнения, ожидая выполнения условия, которое она блокирует. [ Example: Библиотечная функция ввода-вывода, которая блокируется до завершения операции ввода-вывода, может рассматриваться для непрерывной проверки того, завершена ли операция. Каждая такая проверка может состоять из одного или нескольких этапов выполнения, например, с использованием наблюдаемого поведения абстрактной машины. ]end example

[ Note: Из-за этого и предыдущего требования относительно того, какие потоки выполнения должны выполняться в конечном итоге, отсюда следует, что ни один поток выполнения не может выполняться вечно без выполнения шага выполнения. ]end note

Поток выполнения,makes progress когда происходит этап выполнения или выполнение без блокировки не завершается, потому что есть другие параллельные потоки, которые не заблокированы в стандартной библиотечной функции (см. Выше).

Для потока выполненияconcurrent forward progress guaranteesреализация гарантирует, что поток в конечном итоге будет прогрессировать до тех пор, пока он не завершится. [ Note: Это требуется независимо от того, выполнялись или нет другие потоки выполнения (если есть). В конечном итоге выполнение этого требования означает, что это произойдет в неопределенный, но конечный период времени. ]end note

Это определяется реализацией, обеспечивают ли созданные реализациейmain потоки выполнения, которые выполняются, и потоки выполнения, созданные с помощью std​::​thread параллельных гарантий продвижения вперед. [ Note: Рекомендуется, чтобы реализации общего назначения предоставляли эти гарантии. ]end note

Для потока выполненияparallel forward progress guaranteesреализация не требуется, чтобы гарантировать, что поток в конечном итоге будет прогрессировать, если он еще не выполнил ни одного шага выполнения; после того, как этот поток выполнил шаг, он предоставляет гарантии одновременного продвижения вперед.

[ Note: Здесь не указывается требование о том, когда запускать этот поток выполнения, которое обычно указывается сущностью, создающей этот поток выполнения. Например, поток выполнения, который обеспечивает одновременные гарантии продвижения вперед и выполняет задачи из набора задач в произвольном порядке, одну за другой, удовлетворяет требованиям параллельного продвижения вперед для этих задач. ]end note

Для потока выполненияweakly parallel forward progress guaranteesреализация не гарантирует, что поток в конечном итоге будет прогрессировать.

[ Note: Нельзя ожидать, что потоки выполнения, обеспечивающие слабо параллельные гарантии продвижения вперед, будут прогрессировать, независимо от того, работают ли другие потоки или нет; однако блокировка с делегированием гарантии продвижения вперед, как определено ниже, может использоваться для обеспечения того, чтобы такие потоки выполнения в конечном итоге достигли прогресса. ]end note

Гарантии одновременного продвижения вперед сильнее, чем гарантии параллельного продвижения вперед, которые, в свою очередь, сильнее, чем гарантии слабо параллельного продвижения вперед. [ Note: Например, некоторые виды синхронизации между потоками выполнения могут выполняться только в том случае, если соответствующие потоки выполнения обеспечивают параллельные гарантии продвижения вперед, но не смогут достичь прогресса при слабо параллельных гарантиях. ]end note

Когда поток выполненияP указывается дляblock with forward progress guarantee delegation завершения набораS потоков выполнения, то в течение всего времениP блокировки Sреализация должна гарантировать, что гарантии продвижения вперед, предоставляемые по крайней мере одним потоком выполнения вS , по крайней мере столь же сильны, какPи гарантии продвижения вперед. [ Note: Не указано, какой поток или потоки выполненияS выбраны и для какого количества шагов выполнения. Усиление не является постоянным и не обязательно действует до конца жизненного цикла затронутого потока выполнения. ПокаP он заблокирован, реализация должна в конечном итоге выбрать и потенциально усилить поток выполнения вS. ] Как только поток выполнения в in завершается, он удаляется из . Когда-то пусто, разблокировано.end noteS SS P

[ Таким образом, Note: поток выполненияB может временно обеспечить более надежную гарантию продвижения вперед на определенное время из-за того, что второй поток выполненияA блокируется на нем с делегированием гарантии продвижения вперед. В свою очередь, еслиB затем блоки с включенной гарантией продвижения впередC, это также может временно обеспечить более сильную гарантию продвижения впередC. ]end note

[ Note: Если все потоки выполнения находятся на стадииS завершения (например, они завершаются и не используют блокирующую синхронизацию неправильно), тоPвыполнение операции, которая блокируется с делегированием гарантии выполнения вперед, не приведетPк эффективному ослаблению гарантии выполнения. ]end note

[ Note: Это не снимает каких-либо ограничений, касающихся блокировки синхронизации для потоков выполнения, обеспечивая параллельные или слабо параллельные гарантии продвижения вперед, потому что реализация не требуется для усиления конкретного потока выполнения, слишком слабая гарантия выполнения которого препятствует общему прогрессу. ]end note

Реализация должна гарантировать, что последнее значение (в порядке модификации), присвоенное атомарной операцией или операцией синхронизации, станет видимым для всех других потоков в течение конечного периода времени.