Считается ли изменение указателя атомарным действием в C?
Если у меня есть многопоточная программа, которая читает кэш-память по ссылке. Могу ли я изменить этот указатель основным потоком, не рискуя другими потоками, читающими неожиданные значения.
Как я понимаю, если изменение является атомарным, другие потоки будут либо читать старое значение, либо новое; никогда не случайная память (или нулевые указатели), верно?
Я знаю, что мне все равно стоит использовать методы синхронизации, но мне все равно любопытно.
Изменения указателя являются атомарными?
Обновление: Моя платформа - 64-битная версия Linux (2.6.29), хотя я также хотел бы получить кроссплатформенный ответ:)
7 ответов
Как уже упоминалось, в языке C нет ничего, что гарантировало бы это, и это зависит от вашей платформы.
На большинстве современных настольных платформ чтение / запись в выровненном месте размером с слово будет атомарным. Но это действительно не решает вашу проблему из-за переупорядочения чтения и записи процессором и компилятором.
Например, следующий код не работает:
Тема А:
DoWork();
workDone = 1;
Нить Б:
while(workDone != 0);
ReceiveResultsOfWork();
Хотя пишите workDone
является атомарным, во многих системах процессор не гарантирует, что запись в workDone
будет виден другим процессорам перед записью, выполненной через DoWork()
видны Компилятор также может свободно переупорядочить запись в workDone
до звонка DoWork()
, В обоих случаях, ReceiveResultsOfWork()
может начать работать с неполными данными.
В зависимости от вашей платформы, вам может понадобиться установить заборы памяти и т. Д., Чтобы обеспечить правильный порядок. Это может быть очень сложно, чтобы получить право.
Или просто используйте замки. Гораздо проще, гораздо проще проверить, как правильно, и в большинстве случаев более чем достаточно.
Язык C ничего не говорит о том, являются ли какие-либо операции атомарными. Я работал над микроконтроллерами с 8-битными шинами и 16-битными указателями; любая операция указателя в этих системах потенциально может быть неатомарной. Я думаю, что помню Intel 386s (некоторые из которых имели 16-битные шины), вызывающие аналогичные проблемы. Кроме того, я могу представить системы, которые имеют 64-битные процессоры, но 32-битные шины данных, что может повлечь за собой аналогичные опасения по поводу операций неатомарного указателя. (Я не проверял, существуют ли такие системы на самом деле.)
РЕДАКТИРОВАТЬ: Майкл ответ стоит прочитать. Размер шины и размер указателя - едва ли единственное соображение относительно атомарности; это был просто первый контрпример, который пришёл мне в голову.
Вы не упомянули платформу. Поэтому я думаю, что немного более точный вопрос будет
Гарантируются ли изменения указателя атомарными?
Различие необходимо, потому что разные реализации C/C++ могут отличаться в этом поведении. Для конкретной платформы возможно гарантировать атомарные назначения и при этом оставаться в пределах стандарта.
Относительно того, гарантировано ли это в целом в C/C++, ответ - нет. Стандарт C не дает таких гарантий. Единственный способ гарантировать присвоение указателя является атомарным - это использовать механизм, специфичный для платформы, чтобы гарантировать атомарность назначения. Например, методы блокировки в Win32 обеспечат эту гарантию.
На какой платформе вы работаете?
Отказ от ответа состоит в том, что спецификация C не требует, чтобы указатель был атомарным, поэтому вы не можете рассчитывать на то, что он атомарный.
Фактическим ответом будет то, что это, вероятно, зависит от вашей платформы, компилятора и, возможно, от расположения звезд в день, когда вы написали программу.
"нормальная" модификация указателя не гарантируется быть атомарной.
отметьте "Сравнить и поменять местами" (CAS) и другие элементарные операции, не являющиеся стандартом C, но большинство компиляторов имеют некоторый доступ к примитивам процессора. в случае GNU GCC есть несколько встроенных функций
Единственное, что гарантировано стандартом, это тип sig_atomic_t.
Как вы видели из других ответов, вероятно, все будет в порядке при нацеливании на общую архитектуру x86, но очень рискованно с более "специализированным" оборудованием.
Если вам действительно не терпится узнать, вы можете сравнить sizeof(sig_atomic_t) с sizeof(int*) и посмотреть, что вы из себя представляете.
Оказывается, это довольно сложный вопрос. Я задал похожий вопрос и прочитал все, на что мне указали. Я многое узнал о том, как работает кэширование в современных архитектурах, и не нашел ничего, что было бы окончательным. Как уже говорили другие, если ширина шины меньше битовой ширины указателя, у вас могут возникнуть проблемы. В частности, если данные попадают за границу строки кэша.
Благоразумная архитектура будет использовать блокировку.