При манипулировании разными индексами массива в 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

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

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