Подтвердить что ты не робот

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

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

Что-то вроде этого (псевдокод):

global array[9000000];

do_something(chunk) {
    for (i = chunk.start; i < chunk.end; i++)
        //do something with array
}

main() {
    chunk1 = {start: 0, end: 5000000};
    chunk2 = {start: 5000000, end: 9000000};

    start_thread(thread1, do_something(chunk1));
    start_thread(thread2, do_something(chunk2));

    wait_for_join(thread1);
    wait_for_join(thread2);
    //do something else with the altered array
}
4b9b3361

Ответ 1

В соответствующем компиляторе С++ 11 это безопасно [intro.memory] (§1.7):

Место памяти является либо объектом скалярного типа, либо максимальным последовательность смежных битовых полей, имеющих ненулевую ширину. [...] Два потоки выполнения (1.10) могут обновлять и получать доступ к отдельной памяти не мешая друг другу.

C11 дает одинаковые гарантии (они даже используют ту же формулировку) в п. 3.4.

В компиляторе С++ 03 это не гарантируется работой по стандарту, но он может по-прежнему работать, если компилятор предоставляет аналогичные гарантии как расширение.

Ответ 2

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

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

(ПРИМЕЧАНИЕ: этот ответ основан на модели памяти С++ 11. Я только что заметил, что вы также спрашиваете о втором языке, я считаю, что C11 указывает очень похожую модель памяти, но не может скажем, что ответ также действителен для C. Для более старых версий обоих языков безопасность потоков была зависимой от реализации.)

Ответ 3

Да, действительно.

TCMalloc является хорошим примером.

Ответ 4

Да.

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

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

Ответ 5

Важная проблема с производительностью!

Правильно, вам не нужна явная блокировка, так как, как говорят другие, местоположение памяти не разделяется.

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

Подход более высокого уровня, такой как OpenMP, устраняет эти проблемы:

  • выравнивание кусков (и номера потоков) настраиваются в соответствии с базовым оборудованием.
  • он обеспечивает лучший контроль над пулом потоков, например, амортизирует стоимость создания потоков.
  • легче писать, а на самом деле менее навязчивым.