У меня есть структура данных в памяти, которая читается несколькими потоками и записывается только одним потоком. В настоящее время я использую критическую секцию, чтобы сделать этот доступ потокобезопасным. К сожалению, это блокирует читателей, даже если к ним обращается только другой читатель.
Есть два способа исправить это:
- использовать TMultiReadExclusiveWriteSynchronizer
- избавьтесь от любых блокировок, используя подход без блокировок
Для 2. У меня пока есть следующее (любой код, который не имеет значения, был исключен):
type
TDataManager = class
private
FAccessCount: integer;
FData: TDataClass;
public
procedure Read(out _Some: integer; out _Data: double);
procedure Write(_Some: integer; _Data: double);
end;
procedure TDataManager.Read(out _Some: integer; out _Data: double);
var
Data: TDAtaClass;
begin
InterlockedIncrement(FAccessCount);
try
// make sure we get both values from the same TDataClass instance
Data := FData;
// read the actual data
_Some := Data.Some;
_Data := Data.Data;
finally
InterlockedDecrement(FAccessCount);
end;
end;
procedure TDataManager.Write(_Some: integer; _Data: double);
var
NewData: TDataClass;
OldData: TDataClass;
ReaderCount: integer;
begin
NewData := TDataClass.Create(_Some, _Data);
InterlockedIncrement(FAccessCount);
OldData := TDataClass(InterlockedExchange(integer(FData), integer(NewData));
// now FData points to the new instance but there might still be
// readers that got the old one before we exchanged it.
ReaderCount := InterlockedDecrement(FAccessCount);
if ReaderCount = 0 then
// no active readers, so we can safely free the old instance
FreeAndNil(OldData)
else begin
/// here is the problem
end;
end;
К сожалению, существует небольшая проблема - избавиться от экземпляра OldData после его замены. Если в методе Read в настоящее время нет другого потока (ReaderCount = 0), его можно безопасно удалить, и все. Но что мне делать, если это не так? Я мог бы просто сохранить его до следующего вызова и утилизировать его там, но планирование Windows теоретически может позволить потоку чтения спать, пока он находится в методе Read и все еще имеет ссылку на OldData.
Если вы заметили какие-либо другие проблемы с приведенным выше кодом, сообщите мне об этом. Это должно выполняться на компьютерах с несколькими ядрами, и вышеупомянутые методы должны вызываться очень часто.
В случае, если это имеет значение: я использую Delphi 2007 со встроенным диспетчером памяти. Я знаю, что диспетчер памяти, вероятно, в любом случае применяет некоторую блокировку при создании нового класса, но я хочу проигнорировать это на данный момент.
Изменить: возможно, из приведенного выше не было ясно: на протяжении всего времени существования объекта TDataManager существует только один поток, который записывает данные, а не несколько, которые могут конкурировать за доступ для записи. Так что это частный случай MREW.