AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi TThreadList benutzen um Variablen im Hauptthread zu schützen

TThreadList benutzen um Variablen im Hauptthread zu schützen

Ein Thema von Frankieboy82 · begonnen am 21. Mai 2008 · letzter Beitrag vom 21. Mai 2008
Antwort Antwort
Seite 1 von 2  1 2   
Frankieboy82

Registriert seit: 21. Mai 2008
12 Beiträge
 
#1

TThreadList benutzen um Variablen im Hauptthread zu schützen

  Alt 21. Mai 2008, 10:20
Hallo,

ich hab da ein ziemliches Problem mit der Synchronisation zweier Threads...
Da es sich bei dem Sourcecode um eine Unit mit einer visuellen Komponente handelt, geht es einmal um den Programm-(oder VCL-)Thread und dann einen zweiten "Arbeits"thread, der von der visuellen Komponente genutzt wird.

Hier mal eine beispielhafte - stark verkürzte - Version:
Delphi-Quellcode:
type
  
  [..]
  
  TWorkThread = class(TThread) //Der Arbeitsthread
  private
    FOwner: TBeispielVC;
    [..]
  protected
    procedure FetchInput_SHARED;
    procedure SendOutput_SHARED;
    [..]
  public
    [..]
    constructor Create( AOwner: TDosCommand );
  end;

TBeispielVC = class(TComponent) //Die visuelle Komponente, die man dem Formular hinzufügt
  private
    [..]
    { Achtung! Die unteren beiden Variablen ("SHARED") werden von beiden Threads
      gleichzeitig benutzt (Datenaustausch). Das Lesen/Ändern (egal von welchem Thread aus)
     ergibt zum Teil Zugriffsfehler! }

    FInputLines_SHARED: TStringList;
    FOutputLines_SHARED: TStrings;
    [..]
    FSync: TMultiReadExclusiveWriteSynchronizer;
  protected
  public
    constructor Create(AOwner: TComponent); override; //Create-Konstruktor
    destructor Destroy; override; //Destroy-Desktruktor
    procedure Execute; //Methode um die Verarbeitung zu starten
    procedure Stop; //Stoppt (beendet!) die Verarbeitung
    [..]
  end;
Kurz zur Erlätuerung des des Ganzen:
TBeispielVC ist die visuelle Komponente, die man einem Forumlar hinzufügt. Sie fungiert eigentlich nur als "Einstellungsspeicher" und übernimmt die Steuerbefehle wie Execute (Verarbeitung starten) oder Stop "Verarbeitung abbrechen" usw...
Die visuelle Komponente gehört logischerweise zum Hauptthread der eigentlichen Delphi-Anwendung.

Die wirkliche Verarbeitung übernimmt ein zusätzlich generierter Thread: TWorkThread. Der Hauptcode dieses Threads ist eine repeat until Schleife, die so lange läuft, bis das ganze vom Benutzer über den VCL-Thread (z.B. mit TBeispielVC.Stop) beendet wird.
-> Das Problem ist der Datenaustausch zwischen den beiden Threads.

TBeispielVC legt die zu übernehmenden Daten in der StringList FInputLines_SHARED (in ihrer eigenen Struktur) ab.
Der Arbeitsthread liest sie dann irgendwann mit der prozedur FetchInput_SHARED aus (wenn er "kann"), kopiert sie in einen eigenen TStringList Puffer und leert dann FInputLines_SHARED - So weiß die VC, dass die Daten übernommen wurden.
Nach dem Verarbeitungsprozess kopiert der Workthread den Output aus einer eigenen TStrings-Liste in die VC-TStrings-Liste FOutputLines_SHARED - per prozedur SendOutput_SHARED. Nach erfolgreichem Abschluss löscht er die Daten in seinem "eigenen" Puffer.
Das passiert wieder und wieder...

Man sieht also: Nur der Workthread liest und schreibt im VCL-Thread. Der VCL-Thread selber wartet nur aufs Abholen oder Anliefern der Daten, hat aber mit der Workthread-Struktur nichts zu tun.

Deswegen/Trotzdem kriege ich laufend Zugriffsverletzungen. Ich habe versucht, den Speicherbereich von TBeispielVC durch einnen MultiWreadExclusiveWriteSynchronizer zu schützen (Variable FSync) - ohne viel Erfolg.
Jedesmal, wenn der Workthread in Input_SHARED oder Output_SHARED liest/schreibt, ruft er vorher FOwner.FSync.BeginRead und danach FOwner.FSync.EndRead (oder BeginWrite und EndWrite) auf, aum eine Kollision zu vermeiden.
Auch wenn TBeispielVC die eigenen beiden Variablen Input_SHARED oder Output_SHARED ändert, wird FSync benutzt.
Ich dachte eigentlich, dass sei die Lösung, aber ich habe immer noch schwerste Zugriffsfehler (Absturz von Delphi)!

Jetzt will ich eine TThreadList benutzen, um die beiden Austausch-Variablen in der Struktur von TBeispielVC zu schützen.
Leider habe ich noch nie mit einer solchen Liste gearbeitet und bräuchte etwas Hilfe:
Wo und wie lege ich die ThreadList an und wie schütze ich damit die SHARED-Variablen? Außerdem muss ich ja noch TThreadList.LockList vom TWorkthread aus aufrufen, wenn von daraus in die beiden besagten Variablen geschrieben oder daraus gelesen wird...

Wenn jemand Erfahrung mit sowas hat, wär ne Hilfe sehr nett!


Grüße,

Frank
  Mit Zitat antworten Zitat
Benutzerbild von spaxxn
spaxxn

Registriert seit: 19. Nov 2004
253 Beiträge
 
Delphi XE2 Enterprise
 
#2

Re: TThreadList benutzen um Variablen im Hauptthread zu schü

  Alt 21. Mai 2008, 10:36
Im Zusammenhang mit TThreadList, befass dich mal mit CriticalSection's

Die TThreadList ist nichts anderes als eine TList, wo eben mit CriticalSection gearbeitet wird.
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#3

Re: TThreadList benutzen um Variablen im Hauptthread zu schü

  Alt 21. Mai 2008, 10:38
Zitat von spaxxn:
Im Zusammenhang mit TThreadList, befass dich mal mit CriticalSection's

Die TThreadList ist nichts anderes als eine TList, wo eben mit CriticalSection gearbeitet wird.
Aber Synchronistation betreibt er ja laut eigener Aussage über TMultiReadExclusiveWriteSynchronizer.

Edit: Für mich stellt sich die Frage, in wie weit die Strings der StringList wirklich kopiert werden. Es wird vielleicht nur der Referenzzähler erhöht. Und was bedeutet das für den Zugriff?
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Benutzerbild von spaxxn
spaxxn

Registriert seit: 19. Nov 2004
253 Beiträge
 
Delphi XE2 Enterprise
 
#4

Re: TThreadList benutzen um Variablen im Hauptthread zu schü

  Alt 21. Mai 2008, 10:43
Kannst du mal die implementation posten?

@sirius: Ich wollte nur verdeutlichen, dass ihm das reine Verwenden von TThreadList zusätzlich das Verwalten von Strings mit ins Haus bringt Da würde das Verwenden der bereits vorhandenen Listen in Verbindung mit CriticalSection's schneller gehen.

Edit: Rechtschreibfehler und so
  Mit Zitat antworten Zitat
Frankieboy82

Registriert seit: 21. Mai 2008
12 Beiträge
 
#5

Re: TThreadList benutzen um Variablen im Hauptthread zu schü

  Alt 21. Mai 2008, 10:54
OK, hier mal die Implementation der beiden wichtigen Prozeduren aus TWorkThread:

Delphi-Quellcode:
procedure TWorkThread.FetchInput_SHARED;
begin
  if FOwner.FInputLines_SHARED.Count > 0 then
  begin
    FInputLines_SHARED.Assign(FOwner.FInputLines_SHARED);
    FOwner.FInputLines_SHARED.Clear;
  end;
end;
Wie man sieht, passiert nicht viel: Enthält der Puffer der VC Strings, dann werden sie übernommen und danach gelöscht.


Delphi-Quellcode:
procedure TWorkThread.SendOutput_SHARED;
begin
  if Assigned(FOwner.FOutputLines_SHARED) then
      begin
        FOwner.FOutputLines_SHARED.BeginUpdate;
        if FOwner.FOutputLines_SHARED.Count = 0 then
          begin
            if (FOutputType = otEntireLine) then
              FOwner.FOutputLines_SHARED.Add(FOutputStr)
            else
              FOwner.FOutputLines_SHARED.Text := FOutputStr;
          end
        else
          begin
            // change the way to add by last addstring type
            if FLineBeginned then
              FOwner.FOutputLines_SHARED[FOwner.FOutputLines_SHARED.Count - 1] := FOutputStr
            else
              FOwner.FOutputLines_SHARED.Add(FOutputStr);
          end;
        FOwner.FOutputLines_SHARED.EndUpdate;
      end;
  FLineBeginned := (FOutputType = otBeginningOfLine);
end;
Das hier ist etwas komplizierter, ich habe es aus einem anderen Quelltext übernommen, und muss es noch vereinfachen.
  Mit Zitat antworten Zitat
Frankieboy82

Registriert seit: 21. Mai 2008
12 Beiträge
 
#6

Re: TThreadList benutzen um Variablen im Hauptthread zu schü

  Alt 21. Mai 2008, 10:57
Zitat von spaxxn:
Da würde das Verwenden der bereits vorhandenen Listen in Verbindung mit CriticalSection's schneller gehen.
Du meinst schneller vom Programmablauf her, oder vom implementieren in den Code?
  Mit Zitat antworten Zitat
EConvertError

Registriert seit: 29. Sep 2003
Ort: Österreich
230 Beiträge
 
#7

Re: TThreadList benutzen um Variablen im Hauptthread zu schü

  Alt 21. Mai 2008, 10:57
Hallo!

Willkommen in der Delphi-Praxis!

Ich würde es mir nicht sooo einfach machen: Gerade im VCL-Thread kann eine CriticalSection ein Einfrieren der Form bedeuten.

Ich kann dir das Tutorial Multithreading The Delphi Way empfehlen. Da lernt man IMHO alles, was man über Threads und Synchronisation wissen sollte.

In Frage kommen je nach genauer Anforderung auch Mutexes, Semaphoren oder der dort vorgestellte "blocking to asynchronous buffer". In diesem Beispiel wird übrigens auch - so ähnlich wie du es willst - mit einer Komponente gearbeitet (TComponent ist aber keine visuelle Komponente). Da müsste man genaueres über deine Anforderungen wissen. Aber wie gesagt: Das Tutorial sollte alle Fragen beantworten.

Man sollte das IMHO irgendwie auch mal in die CodeLib geben oder anderweitig in der DP leicht auffindbar machen...

Mit freundlichen Grüßen,
Andreas
Andreas N.
  Mit Zitat antworten Zitat
Benutzerbild von spaxxn
spaxxn

Registriert seit: 19. Nov 2004
253 Beiträge
 
Delphi XE2 Enterprise
 
#8

Re: TThreadList benutzen um Variablen im Hauptthread zu schü

  Alt 21. Mai 2008, 11:03
Die Implementation würde schneller gehen und vom Ablauf her wäre der Unterschied zu TThreadList nur marginal...

Beides ist definitv nicht die beste Lösung...


Delphi-Quellcode:
procedure TWorkThread.FetchInput_SHARED;
begin
  EnterCriticalSection(MyCritSect);
  if FOwner.FInputLines_SHARED.Count > 0 then
  begin
    FInputLines_SHARED.Assign(FOwner.FInputLines_SHARED);
    FOwner.FInputLines_SHARED.Clear;
  end;
  LeaveCriticalSection(MyCritSect);
end;
Wie man sieht, passiert nicht viel: Enthält der Puffer der VC Strings, dann werden sie übernommen und danach gelöscht.


Delphi-Quellcode:
procedure TWorkThread.SendOutput_SHARED;
begin
  EnterCriticalSection(MyCritSect);
  if Assigned(FOwner.FOutputLines_SHARED) then
      begin
        FOwner.FOutputLines_SHARED.BeginUpdate;
        if FOwner.FOutputLines_SHARED.Count = 0 then
          begin
            if (FOutputType = otEntireLine) then
              FOwner.FOutputLines_SHARED.Add(FOutputStr)
            else
              FOwner.FOutputLines_SHARED.Text := FOutputStr;
          end
        else
          begin
            // change the way to add by last addstring type
            if FLineBeginned then
              FOwner.FOutputLines_SHARED[FOwner.FOutputLines_SHARED.Count - 1] := FOutputStr
            else
              FOwner.FOutputLines_SHARED.Add(FOutputStr);
          end;
        FOwner.FOutputLines_SHARED.EndUpdate;
      end;
  FLineBeginned := (FOutputType = otBeginningOfLine);
  LeaveCriticalSection(MyCritSect);
end;
Zusätzlich musst du noch MyCritSect vom Typ TRTLCriticalSection definieren und
implementation

var
MyCritSect : TRTLCriticalSection;
...
initialization
InitializeCriticalSection(MyCritSect);
finalization
DeleteCriticalSection(MyCritSect);

einfügen.

Das sollte für dich nur ein Test sein. Für eine richtige implementation ist das ein wenig unschön. Damit hast du dann quasi den Ersatz für die TThreadList geschaffen.


constructor Create( AOwner: TDosCommand ); <- warum TDosCommand, wenn man fragen darf?
  Mit Zitat antworten Zitat
Frankieboy82

Registriert seit: 21. Mai 2008
12 Beiträge
 
#9

Re: TThreadList benutzen um Variablen im Hauptthread zu schü

  Alt 21. Mai 2008, 11:13
Danke,

werds mir mal durchlesen!
Hier gehts nämlich wirklich um Geschwindigkeit, und ein eingefrorenes Form hattte ich schon sehr oft

Der Hauptcode im WorkThread ist eine repeat until Schleife, in deren Mitte SendOutput_SAHRED und deren Ende FetchInput_SHARED, aufgerufen werden.
Die Synchronisation ist da nicht so einfach...
Das letzte Mal haben sich der Main-Thread und der Zusatzthread beide komplett blockiert, als ich im Zusatzthread FOwner.FSync.BeginWrite aufgerufen habe. Das Ergebnis war ein Stillstand der gesamten (Delphi-)Applikation.
  Mit Zitat antworten Zitat
Frankieboy82

Registriert seit: 21. Mai 2008
12 Beiträge
 
#10

Re: TThreadList benutzen um Variablen im Hauptthread zu schü

  Alt 21. Mai 2008, 11:15
Zitat von spaxxn:
constructor Create( AOwner: TDosCommand ); <- warum TDosCommand, wenn man fragen darf?
hm, weil Die Komponente eigentlich TDosCommand heißt...
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2   

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:28 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz