C/C++ массивы с потоками - нужно ли использовать мьютексы или блокировки?

Я новичок в использовании потоков и много читал о том, как данные передаются и защиты данных. Но я также не совсем понял, когда мне нужно использовать мьютексы и блокировки для защиты данных.

Ниже приводится описание проблемы, над которой я буду работать. Важно отметить, что это будет критично ко времени, поэтому мне нужно максимально сократить накладные расходы.

У меня есть два двойных массива фиксированного размера.

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

  • Второй массив будет использоваться для хранения результатов расчетов
    в исполнении потоков. Элемент этого массива будет когда-либо обновляться только одним потоком, и, вероятно, только один раз, когда значение результата
    к нему написано.

мои вопросы тогда?

  1. Мне действительно нужно использовать мьютекс в потоке каждый раз, когда я получаю доступ к данным из массива только для чтения? Если да, то можете ли вы объяснить, почему?

  2. Нужно ли использовать мьютекс в потоке, когда он записывает в массив результатов, даже если это будет единственный поток, который когда-либо пишет в этот элемент?

  3. Должен ли я использовать атомарные типы данных, и будет ли у меня значительное время, если я это сделаю?

  4. Похоже, многие ответы на этот тип вопросов - нет, вам не нужен мьютекс, если ваши переменные выровнены. Будут ли выровнены мои элементы массива в этом примере, или есть какой-то способ убедиться, что они есть?

Код будет реализован на 64-битной Linux. Я планирую использовать библиотеки Boost для многопоточности.

спасибо за все ответы и комментарии, которые я обдумывал и просматривал по всей сети в течение нескольких дней, и однажды опубликовав ответ, я получил четкие объяснения буквально за секунды. Существует "принятый ответ", но все ответы и комментарии были одинаково полезны. еще раз спасибо

4 ответа

Решение

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

  1. Нет. Мьютексы не нужны, поскольку потоки только читают массив.

  2. Нет. Поскольку каждый поток выполняет запись только в отдельную ячейку памяти, условие гонки невозможно.

  3. Нет. Здесь нет необходимости в атомарном доступе к объектам. На самом деле, использование атомарных объектов может отрицательно повлиять на производительность, поскольку это предотвращает возможности оптимизации, такие как операции переупорядочения.

  1. Мне действительно нужно использовать мьютекс в потоке каждый раз, когда я получаю доступ к данным из массива только для чтения? Если да, то можете ли вы объяснить, почему?

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

  1. Нужно ли использовать мьютекс в потоке, когда он записывает в массив результатов, даже если это будет единственный поток, который когда-либо пишет в этот элемент?

Зависит.

  1. Если какой-либо другой поток собирается прочитать элемент, вам нужна синхронизация.
  2. Если какой-либо поток может изменить размер вектора, вам нужна синхронизация.

В любом случае, старайтесь не записывать в соседние области памяти разные потоки. Это может разрушить представление. Смотрите "ложный обмен". Учитывая, что у вас, вероятно, нет большого количества ядер и, следовательно, не много потоков, и вы говорите, что запись выполняется только один раз, хотя, вероятно, это не будет серьезной проблемой.

  1. Должен ли я использовать атомарные типы данных, и будет ли у меня значительное время, если я это сделаю?

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

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

  1. ... Будут ли выровнены мои элементы массива в этом примере, или есть какой-то способ убедиться, что они есть?

Как указывает Arvid, вы можете запросить конкретное выравнивание, используя ключевое слово alginas, которое было введено в C++11. До C++ 11 вы можете прибегнуть к конкретным расширениям компилятора: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Variable-Attributes.html

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

В Google есть хорошая информация о гонке, когда вы создаете программы, которые манипулируют данными на общем ресурсе.

Ты на правильном пути.

1) Для первого массива (только для чтения) вам не нужно использовать блокировку мьютекса для него. Поскольку потоки просто читают, не изменяя данные, поток никак не может испортить данные для другого потока.

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

3) Дано определение атомного:

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

Ключевое примечание: блокировка мьютекса является атомарной, что означает, что для захвата / снятия блокировки требуется только 1 инструкция по сборке. Если для захвата / снятия блокировки требуется 2 инструкции по сборке, замок не будет безопасным для потоков. Например, если поток 1 попытался получить блокировку и был переключен на поток 2, поток 2 захватил бы блокировку.

Использование атомарных типов данных уменьшит ваши накладные расходы, но не значительно.

4) Я не уверен, как вы можете убедиться, что ваши переменные выровнены. Поскольку потоки могут переключаться в любой момент в вашей программе (ваша ОС определяет, когда поток переключается)

Надеюсь это поможет

Другие вопросы по тегам