При манипулировании разными индексами массива в C/С++ с двумя потоками необходима синхронизация?
Предположим, что у меня есть массив, определяемый следующим образом:
volatile char v[2];
И у меня есть два потока (обозначаемые соответственно A, B), управляющие массивом v
. Если я гарантирую, что A, B используют разные индексы в любое время, то есть, если A теперь манипулирует v[i]
, тогда B ничего не делает или не манипулирует v[1-i]
. Интересно, нужна ли синхронизация для этой ситуации?
Я упомянул этот вопрос, однако я думаю, что он ограничен на Java. Причина, по которой я задаю этот вопрос, заключается в том, что я изо всех сил пытаюсь справиться со странной и редкой ошибкой в большом проекте в течение нескольких дней, и до сих пор единственная причина, по которой я мог бы объяснить эту ошибку, заключается в том, что синхронизация необходима для выше манипуляции. (Поскольку ошибка встречается очень редко, мне трудно доказать, истинна ли моя гипотеза)
Изменить: как чтение, так и изменение возможны для v
.
Ответы
Ответ 1
Это может быть ошибка компилятора или аппаратное ограничение.
Иногда, когда к памяти обращается менее 32-разрядная/64-битная переменная, процессор считывает 32 бита, устанавливает соответствующие 8 или 16 бит, а затем записывает весь регистр. Это означает, что он будет читать и записывать соседнюю память, что приводит к гонке данных.
Решения
-
используйте инструкции доступа к байтам. Они могут быть недоступны для вашего процессора, или ваш компилятор не знает, как их использовать.
-
введите свои элементы, чтобы избежать такого рода совместного использования. Компилятор должен сделать это автоматически, если ваша целевая платформа не поддерживает доступ к байтам. Но в массиве это конфликтует с ремарками памяти.
- синхронизировать всю структуру
Обсуждение С++ 03/С++ 11
В классическом С++ это вам, чтобы избежать/смягчить такое поведение. В С++ 11 это нарушает реквизиты модели memry, как указано в других ответах.
Ответ 2
Что касается стандартов С++ 11 и C11, ваш код безопасен. С++ 11 §1.7 [intro.memory]/p2, не учитывается примечание:
Место памяти является либо объектом скалярного типа, либо максимальным последовательность смежных битовых полей, имеющих ненулевую ширину. Два или более потоки выполнения (1.10) могут обновлять и получать доступ к отдельной памяти не мешая друг другу.
char
является интегральным типом, что означает его арифметический тип, что означает, что volatile char
является скалярным типом, поэтому v[0]
и v[1]
являются отдельными ячейками памяти.
C11 имеет аналогичное определение в п. 3.4.
Перед С++ 11 и C11 сам язык не имеет понятия потоков, поэтому вы остаетесь на милость конкретной реализации, которую используете.
Ответ 3
Вам необходимо обрабатывать синхронизацию только в том случае, если вы обращаетесь к одной и той же памяти и изменяете ее. Если вы только читаете, вам также не нужно заботиться о синхронизации.
Как вы говорите, каждый поток будет иметь доступ к различным индексам, тогда вам не потребуется синхронизация здесь. но вам нужно убедиться, что два потока не должны изменять один и тот же показатель одновременно.