Hallo Leute,
ich habe folgende Situation mit einem TMultiReadExclusiveWrite-Synchronisierer (MREW):
Delphi-Quellcode:
procedure TMyClass.A;
begin
MREW.BeginRead();
try
//...
finally
MREW.EndRead();
end;
end;
procedure TMyClass.B;
begin
MREW.BeginRead();
try
// ...
// rufe irgendwann evtl. mal über ein paar Ecken TMyClass.A (oder sich selbst rekursiv) auf
// ...
finally
MREW.EndRead();
end;
end;
procedure TMyClass.C;
begin
MREW.BeginWrite();
try
//...
finally
MREW.EndWrite();
end;
end;
Ich habe jetzt 2 Threads, nennen wir sie T1 und T2. T1 ruft öfter mal TMyClass.B auf und T2 öfter mal TMyClass.C. Ich nehme an, dass folgendes passiert:
- T1 ruft B auf, ist aber noch nicht über die oben erwähnten paar Ecken bei A angekommen
- T2 ruft C auf.
- T1 kommt bei A (oder auf einer tieferen Rekursionsebene wieder bei B) an.
Nun befindet sich ja T1 in dem geschützten Read-Block von procedure B, was beduetet das T2 bei BeginWrite() in Methode C warten muss. Nun kommt T1 bei A (oder nochmal bei B) an und wartet bei BeginRead() in A bzw. B, weil vorher eine BeginWrite()-Anfrage eingereiht ist, die aber nicht weiter kommen kann, weil ja T1 den MREW noch lesend in Beschlag hat. Quasi also folgende Reihenfolge:
(T1) MREW.BeginRead();
(T2) MREW.BeginWrite();
(T1) MREW.BeginRead(); --> Deadlock
Ist das so gewollt? D.h. dass zwei Threads, die ansonsten nix miteinander zu tun haben müssen, sich an einem MREW aufhängen können? Das verwundert mich deswegen, weil die Standard-Erklärungen für einen Deadlock normalerweise eine inkonsistente Akquirierung von mindestens 2 Locks oder einen nicht-reentranten Synchronisierer erfordert.
Ich benutze den MREW statt einer CriticalSection, weil ich tendenziell wesentlich mehr lesende als schreibende Aufrufe habe.
Ich bitte einfach mal um ein paar Kommentare dazu: z.B. was ihr in solchen Situationen macht, ob man nicht doch besser eine CriticalSection benutzen sollte oder wie man einen MREW tatsächlich reentrant bekommt.
Viele Grüße