многопоточность — причина для C ++ IntAtomicGet, GotW

в Получил статью № 45, Херб заявляет следующее:

void String::AboutToModify(
size_t n,
bool   bMarkUnshareable /* = false */
) {
if( data_->refs > 1 && data_->refs != Unshareable ) {
/* ... etc. ... */

Это условие if не является потокобезопасным. С одной стороны, оценка даже «data _-> refs> 1» может быть не атомарной; если это так, возможно, что если поток 1 попытается оценить «data _-> refs> 1», а поток 2 обновляет значение refs, значение, считанное из data _-> refs, может быть любым — 1, 2 или даже чем-то это ни оригинальное значение, ни новое значение.

Кроме того, он указывает, что данные _-> ссылки могут быть изменены между сравнением с 1 и сравнением с Unshareable.

Далее мы находим решение:

void String::AboutToModify(
size_t n,
bool   bMarkUnshareable /* = false */
) {
int refs = IntAtomicGet( data_->refs );
if( refs > 1 && refs != Unshareable ) {
/* ... etc. ...*/

Теперь я понимаю, что одни и те же ссылки используются для обоих сравнений, для решения задачи 2. Но почему IntAtomicGet? Я ничего не нашел в поисках по этой теме — все атомарные операции сосредоточены на операциях чтения, изменения, записи, и здесь мы только что прочитали. Так что мы можем просто сделать …

int refs = data_->refs;

…что, вероятно, должно быть просто одна инструкция в конце концов?

2

Решение

Разные платформы дают разные обещания об атомарности операций чтения / записи. x86 например гарантирует, что чтение двойного слова (4 bytes) будет атомная операция. Однако вы не можете предполагать, что это будет верно для любой архитектуры и, вероятно, не будет.

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

1

Другие решения

Чтение из общей памяти (data_->refs) в то время как другой поток пишет в него, это определение гонки данных.

Что происходит, когда мы не атомно читаем из data_->refs в то время как другой поток пытается написать в это же время?

Представь, что нить А делает ++data_->refs (напишите), пока поток B делает int x = data_->refs (читать). Представьте, что поток B читает первые несколько байтов из data_->refs и этот поток А заканчивает записывать свое значение в data_->refs прежде чем поток B закончил чтение. Затем поток B читает остальные байты в data_->refs,

Вы не получите ни исходного значения, ни нового значения; Вы получите совершенно другое значение! Этот сценарий просто иллюстрирует, что подразумевается под:

[…] значение, считываемое из данных _-> refs, может быть любым — 1, 2 или
даже то, что не является ни исходным значением, ни новым значением.

Цель атомарных операций состоит в том, чтобы гарантировать, что операция неделима: она либо выполняется как выполненная, либо не выполняется. Поэтому мы используем атомарную операцию чтения, чтобы гарантировать, что мы получим значение data_->refs либо до его обновления, либо после (это зависит от времени потоков).

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector