Einzelnen Beitrag anzeigen

norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
504 Beiträge
 
Delphi 12 Athens
 
#10

AW: Thread CriticalSection bzw TMultiReadExclusiveWriteSynchronizer pro Property

  Alt 16. Nov 2019, 09:32
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:
  1. Ist der Zugriff auf die Liste sauber geschützt?
  2. Ist der Zugriff auf FWert sauber implementiert?
  3. 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)
  4. 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.
  5. Wird bei mehrfachem Aufruf von TSection.Acquire erkannt, wenn der aus demselben Thread geschieht oder führt auch das zu einer Blockade?
  6. 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
Angehängte Dateien
Dateityp: zip ThrTest.zip (57,3 KB, 6x aufgerufen)
  Mit Zitat antworten Zitat