Wie in GUI einen Thread abfragem

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
NoCee
Beiträge: 122
Registriert: Do 3. Mär 2011, 21:34
OS, Lazarus, FPC: WinXp/7/10 Opensuse13.2 (L 1.8.0 FPC 3.0.4 )
CPU-Target: 32/64Bit
Wohnort: Ulm

Wie in GUI einen Thread abfragem

Beitrag von NoCee »

Hallo zusammen,
ich habe in einem GUI Mainprogramm mehrere Threads in denen serielle Schnittstelle verarbeitet werden.
Momentan polle ich über einen Timer die Threads ab, ob da was empfangen wurde.
Funktioniert so auch.
Was mich jetzt interresieren würde, wie kann ich von einem der Threads selber einen
Event erzeugen um nicht pollen zu müssen.
Ich hab da nicht mal einen Ansatz nach dem ich gockeln könnte.
Könnte mir jemand einen Ansatz für so etwas zeigen nach dem ich suchen kann?

Danke schon mal im Vorraus
Gruß
NoCee
Zuletzt geändert von theo am Di 24. Sep 2019, 09:04, insgesamt 1-mal geändert.
Grund: Korrektur für die Suchfunktion (Threat -> Thread)

corpsman
Beiträge: 1132
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus SVN Trunk, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Wie in GUI einen Threat abfragem

Beitrag von corpsman »

Das Stichwirt heist

Synchronize, kannst mal auf meiner HP ( http://www.Corpsman.de )nach dem Beispiel Race Condition suchen, dort wird gezeigt wie man es Richtig (aber auch Falsch machen) kann.

Aus dem Sample interessiert dich die Routine "IncPrimeCount".

Kurz zusammengefasst.

Wenn du aus einem Thread heraus auf die GUI zugreifen willst, musst du das in einer Synchronized Routine machen, Anstatt die (Parameterlose) Prozedur direkt auf zu Rufen schreibst du :

Code: Alles auswählen

 
Procedure TPrimeSynchronized.IncPrimeCount;
Begin
  inc(PrimeCount); // Zugriff auf LCL erlaubt, informationen müssen via Private Variablen des Threads rein gereicht werden.
End;
 
..TThread.execute..
..
 Synchronize(@IncPrimeCount);
..
 


Du kannst dann von IncPrimeCount aus direkt auf die LCL zugreifen, da die Prozedur innerhalb des Mainthread aufgerufen wird. Dein eigentlicher Thread wartet in der Zwischenzeit.
--
Just try it

mschnell
Beiträge: 3411
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Wie in GUI einen Threat abfragem

Beitrag von mschnell »

TThread.Synchronize ruft eine Funktion im GUI Thread auf und blockiert den aufrufenden Thread so lange, bis die Funktion abgearbeitet ist. Dadurch kann es nicht passieren, dass unerwünschte Effekte durch parallelen Zugriff auf gemeinsame Datenstrukturen auftreten. Allerdings wird der Thread u.U. beliebig lange aufgehalten und kann demzufolge nicht mehr sicher auf seine Trigger (z.B. die Serielle Schnittstelle) reagieren.

TThread.Queue ruft eine Funktion im GUI Thread auf, während der Thread parallel weiterläutf. Man muss selber darauf achten, dass es nicht passiert, das unerwünschte Effekte durch parallelen Zugriff auf gemeinsame Datenstrukturen auftreten.

-Michael

Warf
Beiträge: 1505
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: MacOS | Win 10 | Linux
CPU-Target: x86_64
Wohnort: Aachen

Re: Wie in GUI einen Threat abfragem

Beitrag von Warf »

Du kannst das ganze auch einfach umdrehen, statt im Worker infos aus dem GUI abzufragen kannst du ganz einfach im GUI thread, wenn sich infos ändern, den Worker benachrichtigen. Das ganze dann noch über Critical Sections sperren. Somit musst du nicht irgendwie code in den Mainthread einschläusen (wie es synchronize oder TThread.Queue macht).

Code: Alles auswählen

TWorker = class(TThread)
private
  FSomeData: TSomeData;
  FSomeDataCS: TRTLCriticalSection;
  function getSomeData: TSomeData;
  procedure setSomeData(const newData: TSomeData);
 
...
public
  property SomeData: TSomeData read getSomeData write setSomeData;
...
end;
 
...
function TWorker.getSomeData: TSomeData;
begin
  // wird benötigt wenn FSomeData kein atomarer datentyp ist
  // wenn es einer ist, ist eine CS nicht notwendig
  EnterCriticalSection(FsomeDataCS);
  try
    Result := FSomeData;
  finally
    LeaveCriticalSection(FSomeDataCS)
  end;
end;
 
procedure TWorker.setSomeData(const newData: TSomeData);
begin
  // wird benötigt wenn FSomeData kein atomarer datentyp ist
  // wenn es einer ist, ist eine CS nicht notwendig
  EnterCriticalSection(FsomeDataCS);
  try
    FSomeData := newData;
  finally
    LeaveCriticalSection(FSomeDataCS)
  end;
end;
...


Wenn du jetzt immer die property SomeData verwendest (und nie FSomeData direkt) ist jeder zugriff gesichert, und in deinem GUI kannst du ganz einfach da reinschreiben:

Code: Alles auswählen

procedure TForm1.Edit1Change(Sender: TObject);
begin
  // Angenommen SomeData ist vom typen String
  Worker.SomeData := Edit1.Text;
end;

NoCee
Beiträge: 122
Registriert: Do 3. Mär 2011, 21:34
OS, Lazarus, FPC: WinXp/7/10 Opensuse13.2 (L 1.8.0 FPC 3.0.4 )
CPU-Target: 32/64Bit
Wohnort: Ulm

Re: Wie in GUI einen Thread abfragem

Beitrag von NoCee »

Danke schon mal für die Antworten.

Ich hab jetzt einiges im Netz suchen und lesen können.
Synchronize... ist soweit ich das jetzt kapiert habe für meinen Fall nicht das richtige.
Es soll möglichst kein Thread warten müssen.
Bisher hab ich im Maintread mit einem Timer einen String von den Workerthreads abgefragt ob da irgend etwas drinsteht.
Gesichtert über Critical Section.
Der Gedanke war aus dem Worker eine Prozedur im Mainthread aufzurufen wenn Daten vollständig im Worker da sind.
Unterwegs hab ich jetzt aber den Faden verloren.
Kann ich eine Prozedur vom Main in einem Worker aufrufen?
Die Daten muß ich gegenseitig verriegeln ok, aber eine Prozedur?
Über TThread.Queue hab ich bisher noch nichts gefunden was ich kapiert hätte. Leider das meiste in englisch.
Kann ich zwar, aber da fallen noch viel mehr Wörter die ich noch nicht kenne.

Unterwegs bin ich dann noch auf eine Komponente gestoßen die auf Synaser basiert. Sdpo.
Die machen das aber auch mit Synchronize. Aber da ist schon mal ein komplettes Beispiel dabei.

Gruß
NoCee

NoCee
Beiträge: 122
Registriert: Do 3. Mär 2011, 21:34
OS, Lazarus, FPC: WinXp/7/10 Opensuse13.2 (L 1.8.0 FPC 3.0.4 )
CPU-Target: 32/64Bit
Wohnort: Ulm

Re: Wie in GUI einen Thread abfragem

Beitrag von NoCee »

Hallo zusammen,
ich hab jetzt einiges über TThread.Queue gelesen.
An einer Stelle stand da, das TThread.Queue einen Eintrag in der Mainthread Eventqueue macht.
Das wäre dann eigentlich genau das was ich mir vorgestellt hatte.
Im Event wird dann meine Procedure aufgerufen. Das werd ich mal versuchen
hinzubekommen.
Was mir da nicht einleuchtet, da steht dann ...Queue(@blabla..
Warum steht da immer der Adressoperator?
Der Compilere kennt doch mein komplettes Programm.
Warum muß ich hier mit der Adresse arbeiten?

Danke schon mal für eine Antwort
Gruß NoCee

Socke
Lazarusforum e. V.
Beiträge: 2815
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Wie in GUI einen Thread abfragem

Beitrag von Socke »

NoCee hat geschrieben:Was mir da nicht einleuchtet, da steht dann ...Queue(@blabla..
Warum steht da immer der Adressoperator?
Der Compilere kennt doch mein komplettes Programm.
Warum muß ich hier mit der Adresse arbeiten?

Über den Adressoperator kannst du dem Compiler unterschiedliches sagen:
  • blabla - Rufe die Funktion blabla auf und übergebe deren Rückgabe an TThread.Queue
  • @blabla - Übergebe die Adresse der Methode blabal an TThread.Queue
Im Delphi-Modus erkennt der Compiler die zweite Möglichkeit automatisch; in den anderen Modi musst du dem Compiler explizit sagen, was du willst.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

mschnell
Beiträge: 3411
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Wie in GUI einen Thread abfragem

Beitrag von mschnell »

... Wie immer, wenn man eine Funktion/Prozedur als Parameter übergibt. Da ist bei Queue() nichts besonderes.
-Michael

Antworten