Mam strukturę danych w pamięci, która jest odczytywana przez wiele wątków i zapisana przez tylko jeden wątek. Obecnie używam krytycznej sekcji, aby zapewnić bezpieczny dostęp do wątków. Niestety ma to wpływ na blokowanie czytelników, mimo że korzysta z niego tylko inny czytelnik.Zablokuj wiele czytników pojedynczego pisarza
Istnieją dwie opcje do zaradzenia tej:
- użycie TMultiReadExclusiveWriteSynchronizer
- rezygnacji z jakiegokolwiek blokowania za pomocą blokady wolnego podejście
Na 2. Mam następujące dotychczas (kod, który nie ma znaczenia został pominięty):
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;
Niestety istnieje mały problem z pozbyciem się instancji OldData po jej wymianie. Jeśli żaden inny wątek nie znajduje się obecnie w metodzie odczytu (ReaderCount = 0), można go bezpiecznie usunąć i to wszystko. Ale co mogę zrobić, jeśli tak nie jest? Mogę po prostu przechować to do następnego połączenia i usunąć je tam, ale harmonogram systemu Windows może teoretycznie pozostawić wątek czytnika w stanie uśpienia, gdy jest w ramach metody Read i nadal ma odniesienie do OldData.
Jeśli widzisz inny problem z powyższym kodem, proszę powiedz mi o tym. Powinien być uruchamiany na komputerach z wieloma rdzeniami, a powyższe metody należy wywoływać bardzo często.
Jeśli to ma znaczenie: używam Delphi 2007 z wbudowanym menedżerem pamięci. Mam świadomość, że kierownik pamięci prawdopodobnie wymusza pewną blokadę podczas tworzenia nowej klasy, ale chcę to zignorować w tej chwili.
Edycja: Być może nie wynikało to z powyższego: Dla całego okresu istnienia obiektu TDataManager istnieje tylko jeden wątek, który zapisuje dane, a nie kilka, które mogą konkurować o dostęp do zapisu. Jest to szczególny przypadek MREW.
Jestem nieufny wobec samopowstałego kodu bez blokady, prawie niemożliwe jest, aby to naprawić. Jeśli chodzi o oprogramowanie TMREWS: nie ma możliwości obejrzenia czasu użycia na typowych komputerach, ponieważ istnieją różne sposoby ich implementacji, a VCL zapewnia tylko jeden. Aby zapoznać się z artykułem porównującym różne implementacje (w tym synchronizację czasu), zobacz http://www.codeproject.com/KB/threads/testing_rwlocks.aspx – mghie