AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Synchronisieren von hauptthread und Teilthread
Thema durchsuchen
Ansicht
Themen-Optionen

Synchronisieren von hauptthread und Teilthread

Offene Frage von "zorro08"
Ein Thema von zorro08 · begonnen am 8. Mär 2008 · letzter Beitrag vom 10. Mär 2008
Antwort Antwort
zorro08

Registriert seit: 8. Mär 2008
4 Beiträge
 
#1

Synchronisieren von hauptthread und Teilthread

  Alt 8. Mär 2008, 22:37
Hallo da draußen !

In meinem Programm scheinen RTEs daher zu kommen, dass ein bestimmter Thread, nennen wir ihn Thread_A, bestimmte Daten verändert und der Hauptthread, z.B. in einem TimerEvent oder einem form-Refresh manchmal genau in dem Moment auf diese Daten zugreift, wenn es offensichtlich nicht passt.

Ich habe versucht, den Code in der Timerroutine bzw. beim Aufbau der Anzeige des Hauptthreads mit CriticalSection.enter / Leave solange auszusperren, bis der thread_a mit der Berechnung fertig ist.

Aber dies klappt offensichtlich nicht. Der Code im Hauptthread hinter dem "cs.enter" wurde immer ausgeführt. Darin besteht mein Problem.

Weitere Erkentnisse in diesem Zusammenhang:

o Im Hauptthread kann man mehrmals dasselbe criticalSection.enter aufrufen.
o Der thread_a bleibt beim ersten cs.enter stehen und läuft beim nächsten cs.Leave aus dem Hauptthread wieder weiter.
o N x cs.enter erfordern auch N x cs.leave, damit thread_a wieder läuft
o Es gibt einen Totalstillstand, wenn zuerst cs.leave ohne vorheriges cs.enter aufgerufen wird

Vielen Dank vorab für einen Hinweis in dieser Sache.
  Mit Zitat antworten Zitat
Benutzerbild von Dani
Dani

Registriert seit: 19. Jan 2003
732 Beiträge
 
Turbo Delphi für Win32
 
#2

Re: Synchronisieren von hauptthread und Teilthread

  Alt 8. Mär 2008, 23:07
Abend!
  • Menge der Daten, auf die sowohl Thread_A als auch der Hauptthread zugreifen, minimieren.
  • Critical sections nur im Hauptthread verwenden:
    Delphi-Quellcode:
    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
      CS.Enter;
      try
        Label1.Caption := SomeDataToString(Thread_A.SomeData);
      finally
        CS.Leave;
      end;
    end;
  • Lieber dem Thread ein Ereignis geben, welches mit Synchronize() aufgerufen wird.
    Delphi-Quellcode:
    type
      TSyncEvent = procedure(Sender: TObject{; ....}) of object;

      TThread_A = class(TThread)
      private
        FOnSync: TSyncEvent;
        procedure DoOnSync;
      public
        property OnSync TSyncEvent read FOnSync write FOnSync;
      end;

    implementation

    procedure TThread_A.DoOnSync;
    begin
      Assert(Assigned(FOnSync));
      FOnSync(Self);
    end;

    procedure TThread_A.Foo;
    begin
      if Assigned(FOnSync) then Synchronize(DoOnSync);
    end;
  • Den Datenaustausch zwischen Hauptthread und Thread_A in eine Containerklasse auslagern, welche mit einem Singleton des TMultiReadExclusiveWriteSynchronizer den Zugriff regelt.
Dani H.
At Least I Can Say I Tried
  Mit Zitat antworten Zitat
zorro08

Registriert seit: 8. Mär 2008
4 Beiträge
 
#3

Re: Synchronisieren von hauptthread und Teilthread

  Alt 9. Mär 2008, 11:28
Danke für die extrem schnelle Antwort (5 min !, und dass um 24:00)
Ich habe die Anregungen ausgewertet und z.T. sofort getestet, aber sie lösen mein Problem wahrscheinlich nicht:

Zitat:
Menge der Daten, auf die sowohl Thread_A als auch der Hauptthread zugreifen, minimieren.
Aus bestimmten Gründen nur mit großem Aufwand möglich, aber der neue Mechanismus soll ja auch mit großen Datenmengen funktionieren


Zitat:
Critical sections nur im Hauptthread verwendenelphi-Quellcode: markieren
procedure TForm1.Timer1Timer(Sender: TObject);
begin
CS.Enter;
try
Label1.Caption := SomeDataToString(Thread_A.SomeData);
finally
CS.Leave;
end;
end;
Das scheint gerade das Problem zu sein: cs.enter / cs.leave klappen bei mir im Hauptthread eben insofern nicht, dass der Teil nach dem cs.enter immer ausgeführt wird. auch wenn sich thread_a innerhalb der cs befindet und darin die Daten gerade beschrieben werden.

Vielleicht ist es relevant, wie das beschreiben abläuft: Es wird im thread_a in der CS eine Sensorik angesprochen und deren Meßwerte u.a. in eine ASC-Datei geschrieben

Der haupthtread soll dann z.B. in der Timerroutine oder auf BtnClick diese ASC-Datei innerhalb einer CS wieder parsen und die Daten grafisch anzeigen. Da stört es natürlich, wenn thread_a das File gerade schreibt und der Hauptthread schon wieder darauf zugreifen will.

Dies hat komischerweise auch eine Weile geklappt. Neuerdings treten aber totale blockaden auf, die ich von früher nicht kannte.



Zitat:
Lieber dem Thread ein Ereignis geben, welches mit Synchronize() aufgerufen wird.
Delphi-Quellcode: zusammenfalten | markieren
1


Ich würde heute die ganze Sache anders anpacken, aber es handelt sich um alten Code, der noch ein bischen halten soll. Wenn cs.enter / cs.leave klappen würde, wäre ja alles gut.


Zitat:
Den Datenaustausch zwischen Hauptthread und Thread_A in eine Containerklasse auslagern, welche mit einem Singleton des TMultiReadExclusiveWriteSynchronizer den Zugriff regelt.

Habe ich getestet, verhält sich aber in Bezug auf Hautpthread / Thread wie cs.enter/cs.leave, geht also nicht: Ich kann im Hauptthread mehrmals MultiReadExclusiveWriteSynchronizer.BeginWrite aufrufen, wobei der Code dahinter immer abgearbeitet wird.

Ich versuche jetzt mal WaitForMultipleObjects als Warte - Mechanismus.

Für Hinweise bin ich weiterhin dankbar
Tom.
  Mit Zitat antworten Zitat
zorro08

Registriert seit: 8. Mär 2008
4 Beiträge
 
#4

Re: Synchronisieren von hauptthread und Teilthread

  Alt 9. Mär 2008, 18:03
Nach einigen Stunden Beschäftigung mit dem Thema sieht die Situation so aus:

1. Das Sperren des Hauptthreads klappt mit WaitForSingleObject(Event_0,INFINITE). Und zwar so gut, dass er bei falscher Anwendung niemals wieder ans Laufen kommt.

Falsche Anwendung bedeutet:

a) Entweder wurde nicht organisiert, dass mindestens ein Event, z.B. von einem Thread, kommt.
b) Oder mitten in die TimerRoutine platzt dann ein BtnClickEvent, der ebenfalls WaitForSingleObject(Event_0,INFINITE) enthält und damit seinerseits auf einen Event wartet, der später eigentlich von der Timerroutine ausgelöst werden sollte, um die Lock/Unlock-Kette am laufen zu halten.

Klassischer Deadlock !

Die (für mich) richtige Anwendung wäre:

o Es wird ein Vorratsevent direkt nach dem Erzeugen des Events aktiviert

Delphi-Quellcode:
event0 := CreateEvent(nil, False, False, nil);
SetEvent(Event0);
o Dann kommen die Threads oder der hauptthread mittes WaitForSingleObject( Event_0, 10 ) irgenwann zufällig zum Zuge
o nach der Datenbearbeitung oder - Nutzung im Thread/HauptThread wird direkt wieder ein Event aktiviert

Delphi-Quellcode:
//----------------------------------------------
procedure TForm1.Timer1Timer(Sender: TObject);
//----------------------------------------------
var wfmo: DWORD;
    s: string;
    tp2: int64;
    ccc: integer;
begin

  Timer1.Enabled:=FALSE;

  if fn_GetTimerSec(tp1) > 4.0 then
  begin
    if (WaitForSingleObject(Event0,10) = WAIT_OBJECT_0) then
    begin

      pr_StartTimer(tp2);
      ccc:=0;
      repeat
        s1:= 'mainThread 2 Sekunden Arbeit '+__s(ccc);
        label1.Caption := s1;
        label1.refresh;
        inc(ccc);
      until fn_GetTimerSec(tp2) > 2.0;

      pr_StartTimer(tp1);
      s1:= 'mainThread idle';
      SetEvent(Event0);
    end;

  end;

  inc(cc);
  label1.Caption := s1 + IntToStr(cc)+' ';
  label1.refresh;

  Timer1.Enabled:=TRUE;
end;
bzw.

Delphi-Quellcode:
//----------------------------------
procedure TThread_a.Execute;
//----------------------------------
var tp2,tp1: Int64;
    ccc: Integer;
begin
  inherited;
  pr_StartTimer(tp1);
  s1:= 'Thread_a idle ';
  cc:=0;
  pr_StartTimer(tp1);
  while TRUE do
  begin
    sleep(1);

    if Terminated then
    begin
      exit
    end

    if fn_GetTimerSec(tp1) > 4.0 then
    begin
      if WaitForSingleObject(Event0,10) = WAIT_OBJECT_0 then
      begin
        ccc:=0;
        pr_StartTimer(tp2);
        repeat
          s1:= 'Thread_a Event0, 2 Sekunden Arbeit ! '+__s(ccc);
          sleep(1);
          inc(ccc);
        until fn_GetTimerSec(tp2) > 2;
        s1:= 'Thread_a idle ';
        show;
        pr_StartTimer(tp1);
        SetEvent(Event0);
      end;
    end;
    inc(cc);
  end
end;
o Damit kommem alle im richtigen Moment mal dran, insbesondere eben auch der Hauptthread


Meine Abhilfe sieht jetzt so aus:

o Die Teilthreads, aber nicht der hauptthread, werden weiterhin mit CriticalSection voreinander geschützt
o Der HauptThread stellt seine Auswerte/Anzeigeroutine dem Thread per EreignisProzedur zur Verfügung

  auftObj.thread_messen.onMeasThreadProc := pr_ThreadProc; o Der Thread führt diese Routine per Synchronize() aus.

Delphi-Quellcode:
//----------------------------------------------------------------
procedure Tthread_messen.pr_SyncProc;
//----------------------------------------------------------------
begin
  onMeasThreadProc(self)
end;
Delphi-Quellcode:
   if assigned(onMeasThreadProc) then
    begin
      Synchronize(pr_SyncProc);
    end;
o Während der Ausführung kann kein Repaint oder Neuaufbau der Anzeigen erfolgen, da die Ereignisprozedur kein KeepWindowsAlive oder Sleep() enthält.
o Erst wenn die Auswerte/Anzeigeroutine komplett am Stück beendet ist, kommt der HauptThread zum Zuge. Dann ist alles Kritische aber schon erledigt.

Für weitere erhellende Kommentare bin ich selbstverständlich dankbar. Ansonsten scheint mein spezielles Problem durch diese Verfahrensweise gelöst zu sein.
Danke nochmals an Dani für die schnelle Reaktion.
Klasse Forum, klasse Seite !
  Mit Zitat antworten Zitat
Apollonius

Registriert seit: 16. Apr 2007
2.325 Beiträge
 
Turbo Delphi für Win32
 
#5

Re: Synchronisieren von hauptthread und Teilthread

  Alt 9. Mär 2008, 19:41
Nur ganz am Rande:
Zitat:
Delphi-Quellcode:
event0 := CreateEvent(nil, False, False, nil);
SetEvent(Event0);
In CreateEvent kann man auch gleich angeben, dass das Event gesetzt werden soll.
Wer erweist der Welt einen Dienst und findet ein gutes Synonym für "Pointer"?
"An interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function."
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

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

Re: Synchronisieren von hauptthread und Teilthread

  Alt 10. Mär 2008, 08:41
Zitat von zorro08:
Ich habe versucht, den Code in der Timerroutine bzw. beim Aufbau der Anzeige des Hauptthreads mit CriticalSection.enter / Leave solange auszusperren, bis der thread_a mit der Berechnung fertig ist.
Ist (neben dem Syncronize aus der VCL) die einfachste Variante und sollte zuverlässig funktionieren, solange man überall dasselbe Handle verwendet.
Zitat von zorro08:
Aber dies klappt offensichtlich nicht. Der Code im Hauptthread hinter dem "cs.enter" wurde immer ausgeführt. Darin besteht mein Problem.
hmmm, da hst du etwas falsch gemacht.

Zitat von zorro08:
o Im Hauptthread kann man mehrmals dasselbe criticalSection.enter aufrufen.
Jep, da wird nur ein Zähler hochgezählt, deswegen immer in einen finally-Block um das "leave" setzen.
Zitat von zorro08:
o Der thread_a bleibt beim ersten cs.enter stehen und läuft beim nächsten cs.Leave aus dem Hauptthread wieder weiter.
Wenn der Zähler wieder bei 0 ist.
Zitat von zorro08:
o N x cs.enter erfordern auch N x cs.leave, damit thread_a wieder läuft
Ja, Windows kann ja nicht erraten, was es sonst machen sollte.
Zitat von zorro08:
o Es gibt einen Totalstillstand, wenn zuerst cs.leave ohne vorheriges cs.enter aufgerufen wird
Aha! Sowas macht man aber auch nicht. Du sollst ja die Abschnitte auch so kurz wie möglich halten. Und das sollte man überblicken.


Zitat von zorro08:
1. Das Sperren des Hauptthreads klappt mit WaitForSingleObject(Event_0,INFINITE). Und zwar so gut, dass er bei falscher Anwendung niemals wieder ans Laufen kommt.
Sowas schafft man mit allen Syncro-möglichkeiten. Deswegen ist es eben günstiger die Schnittstelle so dünn wie möglich zu machen, und eben am besten gar kein Datenaustausch während der Threadabarbeitung zu machen. Ansonsten möglichst nur eine Syncronisationsart wählen.

Zitat von zorro08:
a) Entweder wurde nicht organisiert, dass mindestens ein Event, z.B. von einem Thread, kommt.
Verstehe ich nicht.
Zitat von zorro08:
b) Oder mitten in die TimerRoutine platzt dann ein BtnClickEvent, ...
Wie machst du denn das? Wenn beide Ereignisse im selben Thread laufen, dann wird doch der Timer nicht vom Button unterbrochen.

Zitat von zorro08:
Die (für mich) richtige Anwendung wäre:
...
Es gibt viele schöne Konzepte. Hier kommt es sehr stark auf den Anwendungsfall an, welches am günstigsten ist.



Zitat von zorro08:
o Der Thread führt diese Routine per Synchronize() aus.
In deinem Codebeispiel (vor allem mit mehren Threads) komme ich nicht ganz mit. Aber egal. Aber du mischst hier zwei Syncronisationsmethoden. Achte darauf, dass die eine vollständig abgeschlossen ist, bevor die nächste startet.



Noch kurz was zum Code:
Delphi-Quellcode:
while TRUE do
  begin
    sleep(1);

    if Terminated then
    begin //warum hier ein begin-end-Block?
      exit
    end
Das geht doch insgesamt auch mit (vorrausgesetzt die Reihenfolge mit dem sleep ist unwichtig)
Delphi-Quellcode:
while not terminated do
  begin
    sleep(1);
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Benutzerbild von Dani
Dani

Registriert seit: 19. Jan 2003
732 Beiträge
 
Turbo Delphi für Win32
 
#7

Re: Synchronisieren von hauptthread und Teilthread

  Alt 10. Mär 2008, 14:30
Vielleicht noch eine Idee. Anstelle eines TTimers könntest du auch den Thread als Timer benutzen, also den Aufruf des Synchronisations-Ereignises von der vergangenen Zeit abhängig machen. (falls das die Sache irgendwie vereinfacht)
Dani H.
At Least I Can Say I Tried
  Mit Zitat antworten Zitat
zorro08

Registriert seit: 8. Mär 2008
4 Beiträge
 
#8

Re: Synchronisieren von hauptthread und Teilthread

  Alt 10. Mär 2008, 17:14
Danke für die bisherigen Hinweise !

Mein Hauptproblem bestand offensichtlich in der Annahme, dass auch im Hauptthread mehrere scheinbar parallelen Prozesse wie

o Timer-Routinen in verschiedenen gleichzeitig sichtbaren Fenstern
o dazwischen auch mal ein BtnClick usw., jeweils mit gewisser Bearbeitungszeit

mit CS.enter/cs.Leave voreinander geschützt werden können.

Das geht - nun ist mir das klar - nicht, weil immer derselbe Thread = Hauptthread sperren soll, dabei aber nur irgendeinen Zähler im CS-Objekt verändert. Alles andere wäre ja auch komplette unsinnig, da sofort ein Deadlock entstehen würde.

Das im Moment noch Rätselhafte für mich ist allerdings, dass durch den Aufruf von cs.enter im Haupthtread bei gleichzeitig laufenden Nebenthreads mit CriticalSections das Programm komplett blockiert wird, also nicht nur die mit cs.enter geschützten Nebenthreads blockieren. Zu einem cs.leave komme ich dann gar nicht mehr.

Noch merkwürdiger: das scheint erst seit kurzem aufzutreten. Ich werde aber nochmals testen, ob das im Zusammenhang mit dem Aufruf von Mainthread-Methoden z.B. einer Form auftritt. Es könnte sein, dass dann gerade die Zeile
Synchronize(form1.DoEtwas) diesen Effekt bewirkt.


Ich habe mittlerweile den Rat u.a. von Dani beherzigt:

Kritischen Abschnitte des Hauptthreads werden in einen Event gelegt, der vom Nebenthread ausgelöst wurde.

Innerhalb des Events wird dann die Aufgabe ohne Aufruf von KeepWindowsAlive erledigt, so dass parallel dazu weder ein anderer Thread noch der Hauptthread reinspucken kann.

Das scheint zu klappen.

Danke noch mal an alle !
  Mit Zitat antworten Zitat
Antwort Antwort


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 16:45 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