Problème de Thread

Résolu
cs_Jean_Jean Messages postés 615 Date d'inscription dimanche 13 août 2006 Statut Membre Dernière intervention 13 décembre 2018 - 24 avril 2012 à 23:35
cs_Jean_Jean Messages postés 615 Date d'inscription dimanche 13 août 2006 Statut Membre Dernière intervention 13 décembre 2018 - 27 avril 2012 à 07:35
Bonjour les Delphistes,

J'ai un souci dans un Thread. Je souhaite réaliser une série de voyants clignotants dans un Thread dans lequel je créé le Timer dynamiquement. Mes déclarations sont les suivantes:

type
  TClignotant = class(TThread)
  private
    aTimer    : TTimer;
    procedure TimerTimer(Sender: TObject);
  public
    constructor Creat(CreateSuspended:boolean);
    destructor  Destroy; override;
  protected
    procedure   Execute; override;
  end;

constructor Tclignotant.Create(CreateSuspended:boolean);
begin
  aTimer          := TTimer.Create(nil);
  aTimer.Interval := 1;
  aTimer.Enabled  := False;
  aTimer.OnTimer  := TimerTimer;
  inherited Create(CreateSuspended);
  FreeOnTerminate := false;
  Priority        := tpNormal;
end;

destructor  Tclignotant.Destroy;
Begin
  aTimer.Free;
  inherited;
End;

procedure Tclignotant.Execute;
begin
  aTimer.Enabled := TRUE;
  repeat
    Sleep(500);
//    Synchronize(TimerTimer);
  until Terminated;
end;


Synchronize(TimerTimer) n'est pas accepté à la compil
procedure  Tclignotant.TimerTimer(Sender: TObject);
begin
  ...
  with FormIntegrite do
  begin
    sleep(200);
    Img1.Picture := nil;
    if VoyantBool then ImageList1.GetBitmap(0,Img1.Picture.Bitmap)
                  else ImageList1.GetBitmap(1,Img1.Picture.Bitmap);
    Voyantbool := Not VoyantBool;
    Img1.Update;
  end;
  ...
end;
End;


Le Tread est déclaré dans la Fiche Principale classiquement:

...
var
  FormIntegrite : TFormIntegrite;
  Leds          : TClignotant; // Thread pour les voyants clignotent pendant les opérations
...
implementation
...
Procedure TFormIntegrite.VERIF_INTEGRITE_BD(Var FicLog : string);
...
begin
...
  Screen.Cursor := crHourGlass;
  Leds.Resume; // Lancement du clignotement
...// Opérations longues
  Leds.Suspend; // Suspend le clignotement
End;
...
initialization
  Leds := TClignotant.Create(true);
finalization
  Leds.Free;
end.




Question 1 : Le problème c'est pendant l'analyse longue des fichiers, le voyant ne clignote pas. Il ne se met à clignoter qu'après la fin du traitement
Question 2 : Dans la procédure exécute du Thread, je devrai avoir l'instruction
Synchronize(TimerTimer)
Mais j'ai un message d'erreur "Aucune version surchargée de synchronize à la compilation du fait que TimerTimer a comme paramètre Sender : TObject. Mon problème doit venir de là! Le Thread ne fonctionne pas pendant l'exécution de ma procédure longue...

Si quelqu'un avait une idée, ça me rendrait service... Je n'ai pas trouvé d'exemple sur la toile...


Bien à vous!
Jean_Jean

15 réponses

Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
25 avril 2012 à 14:07
Salut,

ou tu utilises un Timer ou un Thread ... mais les deux ne me semble judicieux.

voici un exemple tout simple avec un Thread qui change la couleur d'un TShape de rouge à vert avec un intervalle (exprimé en millisecondes) réglable grace à une TTrackBar
[hr]unit UThreadTest;


[b]interface

uses
  /bWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ComCtrls;

[b]type
  /bTfrmThreadTest =   class (TForm)
    Shape1: TShape;
    btnStopThread: TButton;
    TrackBar1: TTrackBar;
    procedure FormCreate(Sender: TObject);
    procedure btnStopThreadClick(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  [b]private
    /b { Déclarations privées }
  [b]public
    /b{ Déclarations publiques }
    procedure UpdateShape;
  end;

  TMyThread  = class(TThread)
  [b]protected
    procedure /bExecute; override;
  end;
[b]var
  /bfrmThreadTest: TfrmThreadTest;
  MyThread: TMyThread;
  ShapeOn: Boolean =  True;
  Interval: Integer = 500;
[b]implementation

/b{$R *.dfm}

{ TForm7 }

procedure  TfrmThreadTest.btnStopThreadClick(Sender: TObject);
[b]begin
  /bMyThread.Terminate;
end;

procedure TfrmThreadTest.FormCreate(Sender: TObject);
[b]begin
  /bMyThread : = TMyThread.Create(True);
  MyThread.FreeOnTerminate :=  True;
  MyThread.Resume;
end ;

procedure TfrmThreadTest.FormDestroy(Sender: TObject);
[b]begin
  if not /bMyThread.Terminated [b]then
    /bMyThread.Terminate;
end;

procedure TfrmThreadTest.TrackBar1Change(Sender: TObject);
[b]begin
  /bInterval : = TrackBar1.Position;
end;

procedure TfrmThreadTest.UpdateShape;
[b]const
  /bShapeColors : array[Boolean] of TColor =  (clRed, clGreen);
[b]begin
  /bShapeOn :=  not  ShapeOn;
  Shape1.Brush.Color : = ShapeColors[ShapeOn];
end;

{ TMyThread }

procedure TMyThread.Execute;
var I, V : Integer;
[b]begin
  while not /bTerminated [b]do
  begin
    /bV : =  Interval  div  100;
    Synchronize(frmThreadTest.UpdateShape);
    for I : = 0 to 100 [b]do
    begin
      if /bTerminated then Exit;
      Sleep(V);
    end;
  end;
end;

end.
[hr]

le code se passe de commentaires ...
mais si il y a des zones d'ombres ... n'hésitez pas


[hr]@+Cirec
[hr]
3
cs_cantador Messages postés 4720 Date d'inscription dimanche 26 février 2006 Statut Modérateur Dernière intervention 31 juillet 2021 13
25 avril 2012 à 15:21
est lancée automatiquement dans le Activate de la forme.

ne rien mettre également dans le OnActivate
car cet évènement se déclenche de manière intempestive pour un oui ou pour un non..
je suis à peu près certain que c'est ici l'origine de tes soucis.

avec l'exemple fourni par cirec (à qui je fais un petit coucou au passage)

tu devrais pouvoir t'en sortir.

cantador
3
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
25 avril 2012 à 22:48
@Cantador:
petit coucou au re-passage
ça fait longtemps hein
j'espère que tout va bien

[quote=Jean-Jean]1. La tempo est dans le Sleep(V) en ms...
2. Comme tu divises la consigne par 100 avec
Interval div 100, tu compenses le temps en obligeant la scrutation du terminate dans une boucle répétée 100 fois...
C'est curieux comme système /quote
Curieux oui et non:
[quote=l'aide de Delphi]Surchargez Execute et insérez le code à exécuter quand le thread commence. Execute doit tester la valeur de la propriété Terminated afin de déterminer s'il faut sortir du thread. /quote
donc premièrement c'est l'aide de Delphi qui dit qu'il faut s'en servir comme cela
et deuxièmement si tu mets un intervalle de plusieurs secondes (10 sec. par Ex.) ça permet, si au bout de 5 sec., si tu fermes l'application, qu'elle se ferme de suite ... sinon (sans le deuxième teste)
if Terminated then Exit;
elle se fermera qu'à la fin des 10sec. de l'intervalle, puisque la méthode Sleep est une méthode bloquante pour le Thread qui l'exécute.


Sinon j'ai fait le teste ... comme je le pensai l'utilisation d'un Timer dans un Thread est totalement inutile car le Timer et donc son évènement OnTimer tournent dans le Thread principal de l'application et non dans le Thread créé ...
et le fonctionnement de ton code le prouve:[quote=Jean-Jean]le clignotement s'exécute en fin de procédure de ma fiche principale... /quote

voilà je crois que j'ai tout dit

[hr]@+Cirec
[hr]
3
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
27 avril 2012 à 01:15
tu peux mettre la procédure longue dans la fiche principale.

pour le reste ... pour quel code as tu opté ?
tout dépend de ton choix, de qu'il y a dans le Create & le Execute ... etc.


[hr]@+Cirec
[hr]
3

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_cantador Messages postés 4720 Date d'inscription dimanche 26 février 2006 Statut Modérateur Dernière intervention 31 juillet 2021 13
25 avril 2012 à 09:36
Bonjour,

pas d'exemples sur la toile !
il y en a des beaucoup en fait..

je commencerai par commenter les deux sleep..

En attendant voici un exemple simple de mise en oeuvre de Threads dans lesquels tu pourras puiser :

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, SyncObjs, ExtCtrls;

type
  TForm1 = class(TForm)
    Button2: TButton;
    Button3: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Button1: TButton;
    Button4: TButton;
    Button5: TButton;
    CheckBox1: TCheckBox;
    Button6: TButton;
    Button7: TButton;
    Button8: TButton;
    Button9: TButton;
    Button10: TButton;
    Button11: TButton;
    Bevel1: TBevel;
    Bevel2: TBevel;
    Bevel3: TBevel;
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure Button8Click(Sender: TObject);
    procedure Button9Click(Sender: TObject);
    procedure Button10Click(Sender: TObject);
    procedure Button11Click(Sender: TObject);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

var
  Form1: TForm1;

implementation

uses UnitMyThread_2, UnitMyThread_3, UnitMyThread_1, UnitMyThread_4,
  UnitMyThread_5, UnitMyThread_6, UnitMyThread_7, UnitSemaphoreForm,
  UnitMyThread_8;

{$R *.dfm}

procedure TForm1.Button2Click(Sender: TObject);
var
  MyThread_2: TMyThread_2;
begin
  MyThread_2 := TMyThread_2.Create(Label1);
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  MyThread_2b: TMyThread_2;
begin
  MyThread_2b := TMyThread_2.Create(Label2);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  MyThread_1: TMyThread_1;
begin
  MyThread_1 := TMyThread_1.Create(False);
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  MyThread_3: TMyThread_3;
begin
  MyThread_3 := TMyThread_3.Create(False);
end;

procedure TForm1.Button5Click(Sender: TObject);
var
  MyThread_4: TMyThread_4;
begin
  MyThread_4 := TMyThread_4.Create(False);
  repeat
  // boucle vide pour attendre la fin du thread
    Application.ProcessMessages;
  until MyThread_4.ThreadTerminated;
  MessageBox(Handle, 'Le thread est terminé', 'Avertissement', MB_OK);
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  MyThread_4: TMyThread_4;
begin
  MyThread_4 := TMyThread_4.Create(False);
  repeat
    Application.ProcessMessages;
    MyThread_4.Suspended := checkBox1.Checked;
  until MyThread_4.ThreadTerminated;
  MessageBox(Handle, 'Le thread est terminé', 'Avertissement', MB_OK);
end;

procedure TForm1.Button7Click(Sender: TObject);
var
  MyThread_Faible: TMyThread_5;
  MyThread_Prioritaire: TMyThread_5;
begin
  MyThread_Faible := TMyThread_5.Create(False, 'Faible');
  MyThread_Faible.Priority := tpIdle;
  Sleep(500);
  MyThread_Prioritaire := TMyThread_5.Create(False, 'Prioritaire');
  MyThread_Prioritaire.Priority := tpTimeCritical;
end;

procedure TForm1.Button8Click(Sender: TObject);
var
  Thread_1: TMyThread_6;
  Thread_2: TMyThread_6;
  Mutex: THandle;
begin
  Mutex := CreateMutex(nil, False, 'MyMutex');
  Thread_1 := TMyThread_6.Create(False, 'Thread_1');
  Thread_2 := TMyThread_6.Create(False, 'Thread_2');
  CloseHandle(Mutex);
end;

procedure TForm1.Button9Click(Sender: TObject);
var
  SemaphoreForm: TSemaphoreForm;
  Thread_1: TMyThread_7;
  Thread_2: TMyThread_7;
  Thread_3: TMyThread_7;
  Semaphore: THandle;
begin
  SemaphoreForm := TSemaphoreForm.Create(Application);
  SemaphoreForm.Show;
  Semaphore := CreateSemaphore(nil, 1, 1, 'MySemaphore');
  Thread_1 := TMyThread_7.Create(False, 'Thread_1', SemaphoreForm);
  Thread_2 := TMyThread_7.Create(False, 'Thread_2', SemaphoreForm);
  Thread_3 := TMyThread_7.Create(False, 'Thread_3', SemaphoreForm);
  CloseHandle(Semaphore);
end;

procedure TForm1.Button10Click(Sender: TObject);
var
  Thread_1: TMyThread_8;
  Thread_2: TMyThread_8;
  Thread_3: TMyThread_8;
begin
  Thread_1 := TMyThread_8.Create(False, 'Thread_1');
  Thread_2 := TMyThread_8.Create(False, 'Thread_2');
  Thread_3 := TMyThread_8.Create(False, 'Thread_3');
end;

procedure TForm1.Button11Click(Sender: TObject);
var
  Event: TEvent;
begin
  Event := TEvent.Create(nil, False, False, 'MyEvent');
  Event.SetEvent;
end;
end.


cantador
0
cs_Jean_Jean Messages postés 615 Date d'inscription dimanche 13 août 2006 Statut Membre Dernière intervention 13 décembre 2018 3
25 avril 2012 à 10:24
Salut Cantador,

Je vois que tu es monté en grade! Lol!

Merci de me répondre, mais je connais le code exemple que tu me donnes, il ne m'aide pas à comprendre mon bug comme bien d'autres que j'ai rencontré y colmpris sur ce site...

Le sleep de 500 ms est mis par réflexe car nécessaire parait-il pour laisser à windows du temps...
Le sleep de 200 ms est ma tempo de timer.

Je reconnais que je ne suis pas à l'aise avec les Threads...

Bien à toi

Jean_Jean
0
cs_cantador Messages postés 4720 Date d'inscription dimanche 26 février 2006 Statut Modérateur Dernière intervention 31 juillet 2021 13
25 avril 2012 à 11:42
Je vois que tu es monté en grade!
oui !

Pour faire un thread, c'est pas compliqué..
il faut partir d'un exemple simple (et qui fonctionne bien sûr)
et intégrer tes procédures et notamment celle relative à EXECUTE.

Il vaut mieux créer une unité à part pour écrire le Thread et mettre
un use dans le forme principale.

Dans l'exemple fournit, garde uniquement ce qui t'intéresse (vire le reste)
et entre ton code.

juste un point, je n'ai pas mis les 10 unités thread qui vont avec
mais comme tu les as..

tiens moi au courant

ps: inutile de créer un Timer dynamiquement..

bon courage

cantador
0
cs_Jean_Jean Messages postés 615 Date d'inscription dimanche 13 août 2006 Statut Membre Dernière intervention 13 décembre 2018 3
25 avril 2012 à 12:24
C'est ce que j'ai essayé dans tous les sens d'intégrer mon code de timer.
Au début j'ai essayé avec composant timer classique, mais le code qui s'exécute est dans l'évènement OnTimer du composant et non dans exécute. Alors comment synchroniser le timer avec le trhead?

Et je ne sais pas comment piloter le timer dans le exécute : timer1.Enabled := True?

Bon je verrai ça ce soir!

Jean_Jean
0
cs_cantador Messages postés 4720 Date d'inscription dimanche 26 février 2006 Statut Modérateur Dernière intervention 31 juillet 2021 13
25 avril 2012 à 13:02
essaie de mettre d'abord sur le OnCreate :

timer1.enabled := false;

puis effectivement dans le execute :
timer1.enabled := true;

et le remettre à false également dans la procédure finale :
TMyThread.OnTerminateProcedure(Sender: TObject);

il faudrait réfléchir pour le relancer si ce que tu souhaites..

cantador
0
cs_Jean_Jean Messages postés 615 Date d'inscription dimanche 13 août 2006 Statut Membre Dernière intervention 13 décembre 2018 3
25 avril 2012 à 13:31
En mettant dans le create du thread:

FormIntegrite.timer1.enabled := False;

ça plante!

Dans le Execute :

timer1.enabled := true;
ça déclenche le Timer mais comme dans mon code précédent, le clignotement s'exécute en fin de procédure de ma fiche principale...

Ma procédure empêche le Thread de s'exécuter, un comble!

même en mettant un niveau de priorité max au Thread, le résultat est le même. Je pense donc
qu'il y a dans mon code de procédure qlq chose
qui est prioritaire par rapport au Thread.

Je pense que cela ne peut pas venir d'une boucle de parcours de fichier mais peut-être
d'un forçage par Update d'un progressbar.

J'ai aussi un Application.ProcessMessages; avant l'exécution de la procédure car la procédure est lancée automatiquement dans le Activate de la forme.
J'ai été obligé de mettre ce forçage d'affichage car sinon la procédure s'exécutait avant que la fiche ne finisse de s'afficher...

Tordu quand même ce truc...

Bon après-midi

Jean_Jean
0
cs_Jean_Jean Messages postés 615 Date d'inscription dimanche 13 août 2006 Statut Membre Dernière intervention 13 décembre 2018 3
25 avril 2012 à 19:27
Merci les amis pour vos observations...

Je reviens vers vous pour vous dire le résultat de mes tests et en profiterai pour publier un petit code de démo lorsque je serai arrivé au résultat attendu.

Car le problème doit être identifié et servir à d'autres noeuds noeuds comme moi...

Bien à vous

Jean_Jean
0
cs_Jean_Jean Messages postés 615 Date d'inscription dimanche 13 août 2006 Statut Membre Dernière intervention 13 décembre 2018 3
25 avril 2012 à 22:01
@Cirec
Ton code devrait être transposable facilement dans mon cas. Cependant j'aime comprendre ce que je fais. Peux-tu me confirmer ma compréhension de ta boucle dans le Execute:
1. La tempo est dans le Sleep(V) en ms...
2. Comme tu divises la consigne par 100 avec
Interval div 100, tu compenses le temps en obligeant la scrutation du terminate dans une boucle répétée 100 fois...
C'est curieux comme système

procedure TMyThread.Execute; 
var I, V : Integer; 
begin 
  while not Terminated do 
  begin 
    V := Interval div 100; 
    Synchronize(frmThreadTest.UpdateShape); 
    for I := 0 to 100 do 
    begin 
      if Terminated then Exit; 
      Sleep(V); 
    end; 
  end; 
end;


@cantador
J'ai viré le lancement de ma procédure du OnActivate (je pensai qu'il ne s'exécutait qu'une seule fois). J'ai donc lancé ma procédureavec une tempo de 500 ms déclenché dans le OnActivate... Mais le Problème ne venait pas de là...

Ma résolution est en bonne voie

Merci

Jean_Jean
0
cs_cantador Messages postés 4720 Date d'inscription dimanche 26 février 2006 Statut Modérateur Dernière intervention 31 juillet 2021 13
26 avril 2012 à 12:42
[i]ça fait longtemps hein
j'espère que tout va bien/i

oui, merci cirec
tout va bien

cantador
0
cs_Jean_Jean Messages postés 615 Date d'inscription dimanche 13 août 2006 Statut Membre Dernière intervention 13 décembre 2018 3
26 avril 2012 à 19:35
Hello,

Je progresse...Je gère le clignotement et l'affichage dans deux threads différents mais je ne suis pas encore arrivé au bout de mon code car l'opération longue est encore fictive. Il me reste à créer un exemple réel avant que je publie le code.
Dois-je créer mon opération longue dans une procedure normale de la Fiche ou bien dans le Execute d'un 3ème thread?

Il y a quelque chose que je n'arrive pas à gérer non plus, c'est une sortie brutale de l'application (case système) alors que l'opération longue est en cours. Je pensai que le code suivant me mettrai à l'abri d'une erreur mais ça n'est pas le cas:

procedure TForm2.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  if Assigned(MyThread1) then
    if not MyThread1.Terminated then  MyThread1.Terminate;
  if Assigned(MyThread2) then
    if not MyThread2.Terminated then  MyThread2.Terminate;
end;


Bien à vous
Jean_Jean
0
cs_Jean_Jean Messages postés 615 Date d'inscription dimanche 13 août 2006 Statut Membre Dernière intervention 13 décembre 2018 3
27 avril 2012 à 07:35
Merci Cirec pour ta réponse.

C'est bisarre je me doutai que tu répondrai cela...
Comme vous avez répondu au sujet premier de mon post, je valide donc les réponses qui m'ont aidé.

Vaste réflexion que de savoir ou se mettent les opérations longues qui s'enchainent et qui doivent informer l'utilisateur des différentes progressions.

Je travaille dessus. Je remerai un post sur le sujet. J'ai vu le code de Cari par exemple sur l'enchainement de ses traitements longs mais trop compliqué pour moi. Beaucoup d'exemple prennent des exemples fictifs qu'ils traitent dans le execute (comme ton exemple) mais ça ne me convient pas. En effet mon programme réél est assez compliqué et je perdrai en lisibilité si je devai inclure mes procédures de traitement dans des bouts d'unités de thread.

Je préfererai une structure du type:

Fiche principale

Procédure longue 1 = > affichage 1 cumulé de la progression

Procedure longue 2 => Affichage 2 cumulé de la progressuion globale

Thread

Gère les différents affichages cumulés


Merci


Jean_Jean
0
Rejoignez-nous