2013-03-23 2 views
3

В C++ я чувствую, что мне всегда приходилось верить, что такие вещи, как var ++ и var, являются достаточно потокобезопасными - AKA - у вас есть гарантия того, что ваше значение будет увеличиваться или уменьшаться в определенный момент времени.var ++ или var-- not threadsafe?

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

В небольшой программе у меня есть глобальная переменная, инициализированная в 0. Запущено восемь P-Threads, каждый из которых вызывает varname ++ в общей сложности 1024 раза и составляет до 8 * 1024 приращений. Но после того, как все потоки завершили выполнение, значение varname значительно меньше 8 * 1024.

Я пропустил здесь лодку? Может кто-нибудь, пожалуйста, просветит меня?

+0

Почему вы считаете, что 'var ++' и 'var -' являются потокобезопасными? – Xymostech

+5

'++' resp. '--' не являются атомарными операциями, поэтому хранилище измененного значения может быть после того, как другой поток прочитал (и, возможно, изменил) значение. –

+0

@ Xymostech от алгоритмов класса, таких как неблокирующий кольцевой буффер, о котором я раньше думал, что я понял – user2126895

ответ

5

Что именно приведет вас к вере тех, где поточно? В общем, это не так. Причина этого проста: Арифметика, как правило, делается на регистрах, поэтому var++ может быть преобразована в нечто вроде следующего:

load var to R1 
inc R1 
store R1 to var 

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

Наличие нескольких потоков для доступа к одной и той же переменной определяется как гонка данных (и, следовательно, неопределенное поведение) стандартом (C++ 11), если ни один из потоков не изменяет переменную (если все имеют доступ только для чтения, ты в порядке).

Для операций по обеспечению безопасности потоков вам необходимо использовать либо блокировку (например, используя std::mutex в C++ 11), либо атомные операции. Если вы используете C++ 11, вы можете использовать std::atomic<int> (или любой другой тип вашего счетчика) в качестве типа для var, чтобы получить потоковые изменения. (Арифметика) Операции с std::atomic<T> (например, операторы приращения и декремента) гарантируют, что стандарт будет потокобезопасным.

+0

Большинство людей говорят: «Нет, ++ не является атомарным». что очевидно из моего наблюдения.-Но как на самом деле классические вещи, такие как неблокирующие кольцевые буферы, используют их как контроль без коррупции? – user2126895

+0

@ user2126895: Совершенно просто: они этого не делают. То, что нужно делать, - это операция атомарного приращения, подобная той, которая предоставляется «std :: atomic». – Grizzly

+0

Я уважаю вашу перспективу, но алгоритмы без механизмов синхронизации, как это, существуют - в частности, для сценариев однократного чтения с одним чтением. Должно быть, я забыл, как они работают. – user2126895

2

Да, вы пропустили что-то, а именно - что ваши чтения и записи не являются атомарными. Таким образом, количество потоков может считывать значение, затем увеличивать его, а затем записывать обратно, и если все операции выполняются «параллельно», значение будет увеличиваться только на единицу.

Вы можете исправить это с помощью 11 C++ (в или подъеме в) станд :: атомного типа обертка, описанные здесь: http://en.cppreference.com/w/cpp/atomic/atomic

Смежные вопросы