Если у вас есть многопоточное приложение с переменной слова, разделяемой между потоками (например, 32-битный тип данных в 32-битной системе), необходимо ли защищать чтение и запись этого слова с помощью мьютекса?
Мьютексы вокруг чтения и записи одного слова
Ответы (2)
Вместо того, чтобы говорить о кеше, многоядерности, неупорядоченном выполнении и т. д., я просто предложу простой пример, чтобы проиллюстрировать, почему вы хотите использовать мьютекс (или какой-либо другой метод взаимного исключения, например, манипулирование прерываниями и т. д. )
Чтобы увеличить переменную на единицу — по крайней мере, в большинстве архитектур, с которыми я имею дело в эти дни — вы должны прочитать значение из памяти в регистр, настроить значение в регистре и записать его обратно в память. Предположим, у нас есть 2 потока, с высоким приоритетом A и с низким приоритетом B. Возьмем такой сценарий:
- Поток B считывает «x» из памяти в регистр (значение равно 5).
- Поток B увеличивает регистр до значения 6.
- Поток A вытесняет поток B.
- Поток A считывает «x» в регистр (значение по-прежнему 5 в памяти, верно?).
- Поток A увеличивает регистр до значения 6.
- Поток A записывает значение 6 обратно в память
- Поток A переходит в спящий режим.
- Поток B просыпается и записывает значение своего регистра 6 обратно в память.
Теперь оба потока увеличили значение, но оно увеличилось только на единицу.
Если бы это был номер этажа лифта, количество измеренных сердечных сокращений или значение обратного отсчета космического челнока (хорошо, это было бы уменьшением), у нас могла бы возникнуть проблема.
Обратите внимание, что есть некоторые архитектуры (обычно CISC), которые могут выполнять эту операцию приращения атомарно, лучше не делать этого предположения (привет, переносимость и правильность).
Примечание. В некоторых случаях — но не делайте этого — вы можете обойтись без мьютекса, если у вас есть один писатель и несколько читателей. Например, может быть, ISR увеличивает счетчик тиков или что-то еще, и другие потоки/задачи приходят и читают его время от времени.
Думаю, я хочу подчеркнуть, что всегда полезно защищать общие данные, даже если вы думаете, что можете обойтись без них. Если вы хотите «стать коммандос», вам ДЕЙСТВИТЕЛЬНО нужно знать, что вы делаете, и даже в этом случае тот же код может сломаться завтра, когда он будет портирован на новую архитектуру.
В общем, да, вы должны синхронизировать доступ с мьютексом. Даже если вы считаете, что запись в ячейку памяти будет «атомарной», ЦП может изменить порядок инструкций, которые читают и пишут в эту память, вызывая нежелательное поведение. Кроме того, в многоядерной или многопроцессорной системе запись в разделяемую память может быть не сразу видна другим ядрам/ЦП без синхронизации.
Для получения дополнительной информации см. статью Википедии о барьере памяти.