Einzelnen Beitrag anzeigen

Benutzerbild von juergen
juergen

Registriert seit: 10. Jan 2005
Ort: Bönen
1.174 Beiträge
 
Delphi 11 Alexandria
 
#33

AW: Verständnisfrage zur Thread-Synchronisation

  Alt 21. Apr 2022, 12:30
Hallo zusammen,

ich habe mich seit gestern mal tiefer mit Threads beschäftigt und da kam das Beispiel von Stahli hier genau richtig.

Nun habe ich mal eine "Demo" erstellt, mit den 2 verschiedenen Varianten von Stahli und Haentschmann.
In meiner VM-Entwickler-Maschine laufen beide Thread-Varianten so wie erwartet. Man sieht wie das Label refresht wird und das Hochzählen der Zahlen anzeigt.
Auf meinem Haupt-PC läuft es nicht so wie ich es erwarten würde. Das Programm wird blockiert (evtl. selbes Problem wie bei KodeZwerg?), sobald das Programm den Fokus hat oder man mit der Maus nur über das Programm drüber gleitet. Wenn man ein anderes Programm in den Vordergrund holt, zeigt das Label wieder das Hochzählen an und die Blockade ist weg.
Ich hoffe, ich konnte ausdrücken was das Problem ist.

Hier die Hauptunit:
Delphi-Quellcode:
UNIT uMain;

INTERFACE

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

TYPE
  TOnChangeEvent = PROCEDURE( Sender: TObject; MaxValue: Integer; CurrentValue: Integer ) OF OBJECT; { mein Event-Hanlder für den 2. Thread }

  Tfrm_Main = CLASS( TForm )
    Btn_Start_Thread1: TButton;
    CounterLabel1: TLabel;
    Btn_End_Thread1: TButton;
    Label1: TLabel;
    Bevel1: TBevel;
    Btn_Start_Thread2: TButton;
    Btn_End_Thread2: TButton;
    Counterlabel2: TLabel;
    PROCEDURE FormClose( Sender: TObject; VAR Action: TCloseAction );
    PROCEDURE Btn_Start_Thread1Click( Sender: TObject );
    PROCEDURE Btn_End_Thread1Click( Sender: TObject );
    PROCEDURE DoOnChange( Sender: TObject; MaxValue: Integer; CurrentValue: Integer );
    PROCEDURE Btn_Start_Thread2Click( Sender: TObject );
    PROCEDURE Btn_End_Thread2Click( Sender: TObject );
    PROCEDURE FormCreate( Sender: TObject );

  PRIVATE
    { Private-Deklarationen }

  PUBLIC
    { Public-Deklarationen }
  VAR
    gb_ist_Thread1_aktiv, gb_ist_Thread2_aktiv: Boolean;

  END;

VAR
  frm_Main: Tfrm_Main;

IMPLEMENTATION

{$R *.dfm}


USES
  uThread_mit_Erzeugung_Controls_fuer_Zugriff_auf_VCL_im_Hauptthread, uThread_mit_Businesslogic_kennt_somit_nicht_den_HauptThread;


{ mein Event-Handler von dem 2. Thread }
PROCEDURE Tfrm_Main.DoOnChange( Sender: TObject; MaxValue: Integer; CurrentValue: Integer );
BEGIN
  frm_Main.CounterLabel2.Caption := CurrentValue.ToString;
END;

PROCEDURE Tfrm_Main.FormCreate( Sender: TObject );
BEGIN
  gb_ist_Thread1_aktiv := False;
  gb_ist_Thread2_aktiv := False;
END;

PROCEDURE Tfrm_Main.Btn_Start_Thread1Click( Sender: TObject );
BEGIN
  Btn_Start_Thread1.Enabled := False;
  Btn_Start_Thread2.Enabled := False;
  Btn_End_Thread2.Enabled := False;

  FMy_Thread1 := TTheThread.Create( CounterLabel1 );
END;

PROCEDURE Tfrm_Main.Btn_End_Thread1Click( Sender: TObject );
BEGIN
  IF frm_Main.gb_ist_Thread1_aktiv THEN FMy_Thread1.Terminate;
  Btn_Start_Thread1.Enabled := True;
  Btn_End_Thread1.Enabled := True;
  Btn_Start_Thread2.Enabled := True;
  Btn_End_Thread2.Enabled := True;
END;

PROCEDURE Tfrm_Main.Btn_Start_Thread2Click( Sender: TObject );
BEGIN
  Btn_Start_Thread1.Enabled := False;
  Btn_End_Thread1.Enabled := False;
  Btn_Start_Thread2.Enabled := False;

  FMy_Thread2 := TTheThread2.Create;
  FMy_Thread2.OnChange := DoOnChange; { ! }
END;

PROCEDURE Tfrm_Main.Btn_End_Thread2Click( Sender: TObject );
BEGIN
  IF frm_Main.gb_ist_Thread2_aktiv THEN FMy_Thread2.Terminate;
  Btn_Start_Thread1.Enabled := True;
  Btn_End_Thread1.Enabled := True;
  Btn_Start_Thread2.Enabled := True;
  Btn_End_Thread2.Enabled := True;
END;

PROCEDURE Tfrm_Main.FormClose( Sender: TObject; VAR Action: TCloseAction );
BEGIN
  IF frm_Main.gb_ist_Thread1_aktiv THEN FMy_Thread1.Terminate;
  IF frm_Main.gb_ist_Thread2_aktiv THEN FMy_Thread2.Terminate;
END;

END.

Und hier die Unit eines der beiden Thread-Beispiele:

Delphi-Quellcode:
UNIT uThread_mit_Businesslogic_kennt_somit_nicht_den_HauptThread;

INTERFACE

USES
  Winapi.Windows, Winapi.Messages, System.Classes, System.SysUtils, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, uMain;

TYPE

  TTheThread2 = CLASS( TThread )
  PRIVATE
    FOnChange: TOnChangeEvent; { Event-Handler in uMain }
  PUBLIC
    PROPERTY OnChange: TOnChangeEvent READ FOnChange WRITE FOnChange;
    PROCEDURE Execute; OVERRIDE;
  END;

VAR
  FMy_Thread2: TTheThread2;

IMPLEMENTATION

PROCEDURE TTheThread2.Execute;
VAR
  I1, I2: Cardinal;
BEGIN
  I1 := 0;
  I2 := 0;
  TRY
    frm_Main.gb_ist_Thread2_aktiv := True;
    TRY
      WHILE ( NOT Terminated ) DO
      BEGIN
        Inc( I1 );
        IF ( I1 >= 1000 ) THEN
        BEGIN
          Inc( I2 );

          Synchronize(
            PROCEDURE
            BEGIN
              IF Assigned( FOnChange ) THEN
              BEGIN
                FOnChange( Self, I1, I2 ); // Beispiel
              END;
            END );

          I1 := 0;
          IF I2 > 4200000000 THEN I2 := 0; // wegen Gefahr eines Überlaufs wenn jemand mal den Thread laufen lässt...

          // ================================= !!! um Fehler zu testen im(!) Thread ===============================
          // I2 := I2 DIV I1;
        END;
      END;
    FINALLY
      frm_Main.gb_ist_Thread2_aktiv := False;
    END;
  EXCEPT
    /// Wird benötigt, weil eine Exception im(!) Thread diesen Thread beendet und eine Exception im Hauptthread nicht "angezeigt" werden kann
    ON E: Exception DO
    BEGIN
      Queue(
        PROCEDURE
        BEGIN
          MessageBox( Application.MainFormHandle, PChar( Exception.Classname + ' : ' + E.Message ), 'Thread-Error!', MB_OK OR MB_ICONERROR );
        END );
    END;
  END;
END;

END.

Ich würde mich freuen wenn dieses Mysterium aufgeklärt werden könnte.
Vielen Dank schon mal vorab!
Jürgen
Indes sie forschten, röntgten, filmten, funkten, entstand von selbst die köstlichste Erfindung: der Umweg als die kürzeste Verbindung zwischen zwei Punkten. (Erich Kästner)
  Mit Zitat antworten Zitat