Danke noch für das Feedback. Dass
TMultiReadExclusiveWriteSynchronizer
nur bei vielen Lesevorgängen wirklich Sinn macht, war mir bewusst.
Der kurze Sleep kommt daher, dass eingehende Befehle schnell abgearbeitet werden sollen, was aber auch über Event lösbar wäre
Ich habe jetzt meine Anwendung so, dass sie grundsätzlich funktioniert.
Wenn ich aber gewisse Funktionen aktiviere, bekomme ich Deadlocks und weiß nicht so genau warum.
Deshalb habe ich doch noch mal was grundsätzliches.
Zum leichteren Nachvollziehen habe ich ein ein kleines Beispiel konstruiert:
Delphi-Quellcode:
unit uThrTest;
interface
uses
Classes,
SyncObjs;
type
TThreadString =
procedure(Value:
String)
of object;
TthrTest =
class(TThread)
private
FWert:
String;
FListe: TStringList;
FSyncWert: TThreadString;
procedure SyncEventWert;
function GetWert:
String;
procedure SetWert(
const Value:
String);
protected
FSection: TCriticalSection;
procedure Execute;
override;
public
property Wert:
String read GetWert
write SetWert;
property SyncWert: TThreadString
read FSyncWert
write FSyncWert;
constructor Create(CreateSuspended: Boolean);
destructor Destroy;
override;
procedure SendToListe(sValue:
String);
end;
implementation
//******************************************************************************************************************************************
{ TthrProperty }
//******************************************************************************************************************************************
constructor TthrTest.Create(CreateSuspended: Boolean);
//******************************************************************************************************************************************
begin
inherited Create(CreateSuspended);
FSection := TCriticalSection.Create;
Wert := '
';
FListe := TStringList.Create;
end;
destructor TthrTest.Destroy;
//******************************************************************************************************************************************
begin
Terminate;
WaitFor;
FListe.Free;
FSection.Free;
end;
procedure TthrTest.Execute;
//******************************************************************************************************************************************
begin
inherited;
while not Terminated
do
begin
FSection.Acquire;
// Zugriff auf FListe schützen
try
if FListe.Count > 0
then
begin
Wert := FListe[0];
FListe.Delete(0);
end;
finally
FSection.Release;
// Zugriff auf FListe wieder freigeben
end;
TThread.Sleep(300);
end;
end;
procedure TthrTest.SendToListe(sValue:
String);
//******************************************************************************************************************************************
begin
if (sValue <> '
')
then
begin
FSection.Acquire;
// Zugriff auf FListe schützen
try
FListe.Add(sValue);
finally
FSection.Release;
// Zugriff auf FListe wieder freigeben
end;
end;
end;
function TthrTest.GetWert:
String;
//******************************************************************************************************************************************
begin
FSection.Acquire;
// Zugriff auf FWert schützen
try
Result := FWert;
finally
FSection.Release;
// Zugriff auf FWert wieder freigeben
end;
end;
procedure TthrTest.SetWert(
const Value:
String);
//******************************************************************************************************************************************
var
bChanged: Boolean;
begin
FSection.Acquire;
// Zugriff auf FWert schützen
try
bChanged := (FWert <> Value);
if bChanged
then
FWert := Value;
finally
FSection.Release;
// Zugriff auf FWert wieder freigeben
end;
// Nicht innerhalb der CriticalSection da Main sonst blockieren könnte
if bChanged
then
Synchronize(SyncEventWert);
end;
procedure TthrTest.SyncEventWert;
//******************************************************************************************************************************************
begin
if Assigned(FSyncWert)
then
FSyncWert(FWert);
// Zugriff auf FWert da durch Synchronize schon Schutz erzeugt wird
end;
end.
Das Ziel ist:
- Über den Main-Thread soll eine Liste gefüllt werden. Das kann sehr schnell passieren (In meinem Fall Kommandos an ein Gerät. Die Liste dient als Puffer)
- Die Antwort wird in FWert geschrieben. Der Wert wird bei jeder Änderung mit dem Main synchronisiert.
- Zusätzlich soll der Main die Möglichkeit haben schreibend und lesend auf FWert zuzugreifen
Meine Fragen:
- Ist der Zugriff auf die Liste sauber geschützt?
- Ist der Zugriff auf FWert sauber implementiert?
- Welche Möglichkeiten gibt es, dass der SyncWert
zum Main zu keiner Verzögerung führt auch wenn die Abarbeitung im Main länger dauert? (Im Moment löse ich es, dass FWert auf der Main-Seite in eine Liste geschrieben wird, und die abgearbeitet wird)
- Ist es richtig, dass in SyncEventWert
nur auf FWert zugegriffen werden darf (nicht auf Wert)? Ich meine durch den Aufruf von Synchronize(SyncEventWert)
wird schon ein Schutz erzeugt und wenn ich jetzt auf Wert zugreife, kommt der zusätzliche Section-Schutz um GetWert zum Tragen.
- Wird bei mehrfachem Aufruf von TSection.Acquire erkannt, wenn der aus demselben Thread geschieht oder führt auch das zu einer Blockade?
- Ist an meinem Code sonst was falsch?
Zum einfacheren anschauen habe ich den kompletten Beispiel-Code incl. Main-Form als Zip angehängt.
- Durch Klick auf [Write into List] werden Einträge in die Liste geschrieben. Durch schnelles Klicken sieht man, dass Liste langsamer abgearbeitet wird.
- Durch Klick auf [Write into Wert] wird direkt in FWert geschrieben und auch sofort wieder zurück gegeben
- Durch Klick auf [Read Wert] wird FWert direkt gelesen
Vielen Dank
Gerd