C: поток безопасности и порядок операций - PullRequest
0 голосов
/ 21 ноября 2018

Рассмотрим следующий код C:

static sig_atomic_t x;
static sig_atomic_t y;

int foo()
{
    x = 1;
    y = 2;
}

Первый вопрос: может ли компилятор C принять решение «оптимизировать» код для foo до y = 2; x = 1 (в том смысле, что место в памяти для y изменяется перед ячейкой памяти для x)?Это будет эквивалентно, за исключением случаев, когда задействованы несколько потоков или сигналов.

Если ответ на первый вопрос «да»: что мне делать, если я действительно хочу, чтобы гарантия x сохранялась до y

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

Да, компилятор может изменить порядок двух назначений, потому что переупорядочение не является "наблюдаемым" , как определено стандартом C , например, нет никаких побочных эффектов для назначений (снова, как определено стандартом C, который не учитывает существование стороннего наблюдателя).

На практике вам нужен какой-то барьер / ограждение для гарантии заказа, например, используйте услуги, предоставляемые вашей многопоточностьюокружающей среды, или, возможно, C11 stdatomic.h, если доступно.

0 голосов
/ 21 ноября 2018

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

Помимо этих особых случаев, все является честной игрой.Он может выполнить y перед x, он может выполнить их параллельно.Это может оптимизировать весь код, поскольку в коде нет видимых побочных эффектов.И так далее.

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


Если порядок имеет значение, вам следует volatile -квалифицировать переменные.В этом случае языком предоставляются следующие гарантии:

C17 5.1.2.3 § 6 (определение наблюдаемого поведения):

Доступ к изменчивым объектам оценивается строго в соответствии справилам абстрактной машины.

C17 5.1.2.3 § 4:

В абстрактной машине все выражения оцениваются в соответствии с семантикой.

Где«семантика» - это почти весь стандарт, например, часть, которая указывает, что ; состоит из точки последовательности.(В этом случае C17 6.7.6 «Конец полного декларатора является точкой последовательности.» Термин «секвенированный до» определен в C17 5.1.2.3 §3).

Итак, с учетом этого:

volatile int x = 1;
volatile int y = 1;

тогда порядок инициализации гарантированно будет равен x перед y, так как ; в первой строке гарантирует порядок упорядочения, а volatileгарантирует, что программа строго следует порядку оценки, указанному в стандарте.


Теперь, как это происходит в реальном мире, volatile не гарантирует барьеры памяти во многих реализациях компиляторов для многоядерных систем.Эти реализации не соответствуют.

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

...