AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein Delphi TThread mit Queue und Events
Thema durchsuchen
Ansicht
Themen-Optionen

TThread mit Queue und Events

Ein Thema von AJ_Oldendorf · begonnen am 26. Mai 2025 · letzter Beitrag vom 28. Mai 2025
Antwort Antwort
Kas Ob.

Registriert seit: 3. Sep 2023
436 Beiträge
 
#1

AW: TThread mit Queue und Events

  Alt 26. Mai 2025, 11:59
Forgot to shout one thing !

If you are going to use the wait approach right, then make it wait with infinite timeout ! no 250ms or any thing like that, any use of limited time for wait is bad design and shows weak logic with hope to not fail instead will work as intended.
Kas
  Mit Zitat antworten Zitat
AJ_Oldendorf

Registriert seit: 12. Jun 2009
486 Beiträge
 
Delphi 12 Athens
 
#2

AW: TThread mit Queue und Events

  Alt 26. Mai 2025, 12:10
Ok, das "if Terminated" ist überflüssig. Liegt daran, dass der QT zusammen kopiert wurde.
Bitte ignorieren.

Ok, aus Sleep(1), könnte man Sleep(16) machen, da der Aufruf sowieso nie schneller als 16ms (normalerweise) ist.

Das der Aufruf "if FDataQueue.Count > 0 then" von zwei Threads gleichzeitig bearbeitet wird, habe ich nie gesagt.
Die eine Liste ist genau für den einen Thread. Jeder hat sozusagen seine eigene Liste und nur der eine Thread selber macht das Dequeue.

Es wurden wieder einige Dinge gesagt aber auf die konkrete Fragestelle bei meinem Codebeispiel wurde nicht eingegangen (1x Dataqueue, 1x Thread, 2x Events genau an dem Codebeispiel darstellen).
  Mit Zitat antworten Zitat
Edelfix

Registriert seit: 6. Feb 2015
Ort: Stadtoldendorf
248 Beiträge
 
Delphi 12 Athens
 
#3

AW: TThread mit Queue und Events

  Alt 26. Mai 2025, 13:17
Ein Beispiel (ungeprüft):

Hauptformular (Form1)

Delphi-Quellcode:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  WorkerThreadUnit;

type
  TForm1 = class(TForm)
    btnStartThread: TButton;
    btnSendData: TButton;
    btnModeNormal: TButton;
    btnModeIgnore: TButton;
    btnModeAlternative: TButton;
    btnStopThread: TButton;
    MemoLog: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnStartThreadClick(Sender: TObject);
    procedure btnSendDataClick(Sender: TObject);
    procedure btnModeNormalClick(Sender: TObject);
    procedure btnModeIgnoreClick(Sender: TObject);
    procedure btnModeAlternativeClick(Sender: TObject);
    procedure btnStopThreadClick(Sender: TObject);
  private
    FWorker: TMyWorkerThread;
    FDataCounter: Integer;
    procedure LogMessage(const Msg: string);
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FWorker := nil;
  FDataCounter := 0;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Assigned(FWorker) then
  begin
    FWorker.StopThread;
    FWorker.Terminate;
    FWorker.WaitFor;
    FreeAndNil(FWorker);
  end;
end;

procedure TForm1.btnStartThreadClick(Sender: TObject);
begin
  if not Assigned(FWorker) then
  begin
    FWorker := TMyWorkerThread.Create(
      procedure(const Msg: string)
      begin
        LogMessage(Msg);
      end);
    LogMessage('Thread gestartet.');
  end;
end;

procedure TForm1.btnSendDataClick(Sender: TObject);
begin
  if Assigned(FWorker) then
  begin
    Inc(FDataCounter);
    FWorker.EnqueueData('Nachricht #' + FDataCounter.ToString);
  end;
end;

procedure TForm1.btnModeNormalClick(Sender: TObject);
begin
  if Assigned(FWorker) then
  begin
    FWorker.SetProcessingMode(0);
    LogMessage('Modus: Normal');
  end;
end;

procedure TForm1.btnModeIgnoreClick(Sender: TObject);
begin
  if Assigned(FWorker) then
  begin
    FWorker.SetProcessingMode(1);
    LogMessage('Modus: Ignorieren');
  end;
end;

procedure TForm1.btnModeAlternativeClick(Sender: TObject);
begin
  if Assigned(FWorker) then
  begin
    FWorker.SetProcessingMode(2);
    LogMessage('Modus: Alternative Verarbeitung');
  end;
end;

procedure TForm1.btnStopThreadClick(Sender: TObject);
begin
  if Assigned(FWorker) then
  begin
    FWorker.StopThread;
    FWorker.Terminate;
    FWorker.WaitFor;
    FreeAndNil(FWorker);
    LogMessage('Thread gestoppt.');
  end;
end;

procedure TForm1.LogMessage(const Msg: string);
begin
  TThread.Queue(nil, procedure begin
    MemoLog.Lines.Add(Msg);
  end);
end;

end.
Worker-Thread (WorkerThreadUnit)

Delphi-Quellcode:
unit WorkerThreadUnit;

interface

uses
  System.Classes, System.SysUtils, System.SyncObjs, System.Generics.Collections;

type
  TMyWorkerThread = class(TThread)
  private
    FQueue: TThreadedQueue<string>;
    FTerminateEvent: TEvent;
    FProcessingMode: Integer;
    FModeLock: TCriticalSection;
    FLogProc: TProc<string>;
  protected
    procedure Execute; override;
  public
    constructor Create(LogProc: TProc<string>);
    destructor Destroy; override;

    procedure EnqueueData(const AData: string);
    procedure SetProcessingMode(AMode: Integer);
    procedure StopThread;
  end;

implementation

constructor TMyWorkerThread.Create(LogProc: TProc<string>);
begin
  inherited Create(False);
  FQueue := TThreadedQueue<string>.Create(100);
  FTerminateEvent := TEvent.Create(nil, True, False, '');
  FModeLock := TCriticalSection.Create;
  FLogProc := LogProc;
end;

destructor TMyWorkerThread.Destroy;
begin
  FQueue.Free;
  FTerminateEvent.Free;
  FModeLock.Free;
  inherited;
end;

procedure TMyWorkerThread.EnqueueData(const AData: string);
begin
  FQueue.Enqueue(AData);
end;

procedure TMyWorkerThread.SetProcessingMode(AMode: Integer);
begin
  FModeLock.Acquire;
  try
    FProcessingMode := AMode;
  finally
    FModeLock.Release;
  end;
end;

procedure TMyWorkerThread.StopThread;
begin
  FTerminateEvent.SetEvent;
end;

procedure TMyWorkerThread.Execute;
var
  Data: string;
  Mode: Integer;
begin
  while not Terminated do
  begin
    if FTerminateEvent.WaitFor(0) = wrSignaled then
      Break;

    if FQueue.Dequeue(Data, 100) = TWaitResult.wrSignaled then
    begin
      FModeLock.Acquire;
      try
        Mode := FProcessingMode;
      finally
        FModeLock.Release;
      end;

      case Mode of
        0: FLogProc('Verarbeite: ' + Data);
        1: FLogProc('Ignoriert: ' + Data);
        2: FLogProc('Alternative Verarbeitung: ' + Data);
      end;
    end
    else
    begin
      Sleep(10);
    end;
  end;

  FLogProc('Thread beendet.');
end;

end.
Oder habe ich es falsch verstanden?
  Mit Zitat antworten Zitat
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.157 Beiträge
 
Delphi 10.3 Rio
 
#4

AW: TThread mit Queue und Events

  Alt 26. Mai 2025, 15:04
Oje...

Rule 1 - ein Thread hat kein Sleep!

Auch der Zugriff auf Count muss gelocked werden.

Sodo Code.

Thread:

Delphi-Quellcode:
While not Terminated do
  begin
    if Queue.Count = 0 then
      E_Event.WaitFor(INFINITE);

    if not terminated then
      begin
        // DeQueue
      end;
  end;
Queue:

Delphi-Quellcode:
Procedure Enqueue(aData : TData);
begin
  TMonitor.Enter;
  try
    fQueue.Add(aData);
  finally
    TMonitor.Leave;
  end;

  E_Event.SetEvent;
end;
Den E_Event des Threads wird and die Queue übergeben.

Nicht vergessen:

Delphi-Quellcode:
procedure TMyThread.Terminate;
begin
  inherited Terminate;

  E_Event.SetEvent;
end;
Hier nochmal den Link zu meiner CodeRage 2020 Session zum Thema Threads & Queues!

Mavarik

Geändert von Mavarik (26. Mai 2025 um 17:02 Uhr)
  Mit Zitat antworten Zitat
AJ_Oldendorf

Registriert seit: 12. Jun 2009
486 Beiträge
 
Delphi 12 Athens
 
#5

AW: TThread mit Queue und Events

  Alt 27. Mai 2025, 05:27
Danke, beide Beispiele helfen um es besser zu verstehen und es evtl. zu implementieren.
@Mavarik: Dein Video werde ich mir mal angucken, kannte ich ehrlich gesagt nicht
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.185 Beiträge
 
Delphi 12 Athens
 
#6

AW: TThread mit Queue und Events

  Alt 27. Mai 2025, 09:31
Oje...
Rule 1 - ein Thread hat kein Sleep!
Hmmm, meinst Du damit, er SOLLTE besser kein Sleep einsetzen? .... oder er kann gar kein Sleep ausführen?

https://docwiki.embarcadero.com/Libr....TThread.Sleep

https://docwiki.embarcadero.com/RADS...ervice_Threads

Delphi-Quellcode:
//Add the following code to the .cpp file for the TSparkyThread Execute method (the thread function):

void __fastcall TSparkyThread::Execute() {
    while (!Terminated) {
        Beep();
        Sleep(500);
    }

}
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.991 Beiträge
 
Delphi 12 Athens
 
#7

AW: TThread mit Queue und Events

  Alt 27. Mai 2025, 10:05
Ein Thread funktioniert problemlos mit Sleep. Wenn es rein um eine Verzögerung geht, sehe ich auch keinen Grund, das mit Events oder ähnlichem umzusetzen. Das Ergebnis ist das gleiche, ob ich auf ein Event eine bestimmte Zeit bis zum Timeout warte oder dies mit Sleep direkt mache.

Ich persönlich finde den zusätzlichen Code für z.B. ein Event nicht gerade gut für die Lesbarkeit, denn das Event hat ja selbst dann gar keine Funktion.

Anders sieht es aus, wenn man nicht die Verzögerung an sich möchte (z.B. zur Reduktion der CPU-Last), sondern eigentlich auf etwas wartet, das man per Event signalisieren kann. Das ist aber nicht immer möglich. Dann macht das Event natürlich deutlich mehr Sinn, weil man dann gezielt warten und weitermachen kann.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
AJ_Oldendorf

Registriert seit: 12. Jun 2009
486 Beiträge
 
Delphi 12 Athens
 
#8

AW: TThread mit Queue und Events

  Alt 27. Mai 2025, 10:58
Danke Sebastian, würde ich auch so sehen wie du.
Das Codebeispiel oben ist ja von dir, dass will ich jetzt mal noch mit 2 unterschiedlichen Events ausstatten und dann damit mal testen
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.185 Beiträge
 
Delphi 12 Athens
 
#9

AW: TThread mit Queue und Events

  Alt 27. Mai 2025, 11:25
... sehe ich auch keinen Grund, das mit Events oder ähnlichem umzusetzen. Das Ergebnis ist das gleiche, ...
Vielleicht wird darauf abgezielt, dass Windows Sleep nicht unterbrechbar ist und generell die Events schon?
Deshalb könnte es mit Events insgesamt reaktiver werden und auch sicherer gegen Deadlocks, weil Events sich von aussen killen lassen.

Die Timer-Präzision sollte nicht den Unterschied machen, das ist meiner Meinung nach gleich.
Nur bei Cross-Plattform gibt es da generell Vorteile, aber dann auch bei Sleep und WaitFor gleichermaßen.
  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 20:55 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz