Что блокировать и что не блокировать в многопоточной среде (семафоры и разделяемая память)

Я реализовывал простую программу Producer/Consumer, в которой были семафоры и разделяемая память. Для простоты предположим, что в моей программе есть только блок разделяемой памяти и семафор.

Поначалу я думал, что критическим разделом мне нужно считать только фрагменты кода, которые попытаются записать в разделяемый блок памяти. Но поскольку блок разделяемой памяти состоит, скажем, из 1024 байт, я не могу прочитать все данные одновременно (это не атомарная операция), так что действительно возможно, что пока я читаю из него, Producer приходит и начинает писать в нем, так что читатель получит половину старых данных, половину новых данных. Исходя из этого, я могу только подумать, что мне также нужно поместить логику чтения общей памяти в блок «семафор».

Теперь у меня есть много кода, который выглядит так:

if (sharedMemory[0] == '0') { ... }

В этом случае я просто ищу один символ в памяти. Я думаю, мне не нужно беспокоиться об установке семафора вокруг этого, не так ли?

А что, если вместо этого у меня будет что-то вроде

if (sharedMemory[0] == '0' && sharedMemory[1] == '1') { ... }

С моей точки зрения, я предполагаю, что, поскольку это две операции, я должен рассматривать это как критический раздел, поэтому мне нужно поместить вокруг него семафор. Я прав?

Спасибо!


person devoured elysium    schedule 14.11.2010    source источник


Ответы (2)


Технически, в многоядерной или многопроцессорной системе единственное, что является атомарным, — это ассемблерные коды операций, которые специально задокументированы как атомарные. Даже чтение одного байта представляет собой (довольно малую) вероятность того, что другой процессор придет и изменит его до того, как вы его прочитаете, за исключением некоторых случаев, связанных с кешем ЦП и выровненными фрагментами памяти (забавная ветка: http://software.intel.com/en-us/forums/showthread.php?t=76744, Интересно прочитать: http://www.corensic.com/CorensicBlog/tabid/101/EntryId/8/Memory-Consistency-Models.aspx)

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

(Ответ может немного измениться на платформах IL, таких как .NET и JVM, поскольку они дают свои собственные гарантии того, что является атомарным, а что нет).

person Donnie    schedule 14.11.2010
comment
Я так и не понял, к чему ваш пост? Должен ли я заблокировать все? - person devoured elysium; 14.11.2010
comment
Функции, задокументированные как атомарные, конечно же, будут атомарными (при условии, что они не содержат ошибок). - person Jeremy Friesner; 14.11.2010

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

person spstanley    schedule 14.11.2010