Thread

Signaler
Messages postés
220
Date d'inscription
lundi 30 octobre 2000
Statut
Membre
Dernière intervention
15 juillet 2013
-
Messages postés
2527
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
16 octobre 2019
-
Bonjour lecteurs de ce poste
J'ai un petit souci avec les threads...
J'ai un thread plus que classique avec un traitement d'une image assez long. D'où l'utilisation d'un thread.
Je dessin dans un Tbitmap local au thread, puis, une fois terminé le traitement, je colle le résultat dans un autre Tbitmap appartenant à ma fenêtre principale.
Et sur cette dernière, un composant TPaintBox est censé redessiner le résultat.
Et là, rien, un joli rectangle blanc...

Et pourquoi????


Merci..


je vous donne quand même le code :

unit Unit1;

interface

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

type
  TToto=class(tthread)
  private
   bmp:tbitmap;
   procedure synchro;
  protected
    procedure Execute; override;
  end;

  TForm1 = class(TForm)
    PaintBox1: TPaintBox;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure PaintBox1Paint(Sender: TObject);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
    bmpglobal:tbitmap;
  end;

var
  Form1: TForm1;
  toto:TToto;


implementation

{$R *.dfm}



procedure TToto.synchro;
begin
 form1.bmpglobal.canvas.Draw(0,0,bmp);
end;


procedure TToto.Execute;
var i,j,n:integer;
begin
 bmp:=tbitmap.Create;
 bmp.width:=form1.bmpglobal.Width;
 bmp.Height:=form1.bmpglobal.Height;
 while not Terminated do
  begin
   n:=random(500);
   for i:=0 to bmp.Width-1 do
    for j:=0 to bmp.Height-1 do
     bmp.canvas.Pixels[i,j]:=i*j*n;
   synchronize(synchro);
   form1.PaintBox1.Repaint;
  end;
 bmp.free;
end;



procedure TForm1.FormCreate(Sender: TObject);
begin
 DoubleBuffered:=true;
 bmpglobal:=tbitmap.Create;
 bmpglobal.Width:=paintbox1.Width;
 bmpglobal.height:=paintbox1.height;
 toto:=TToto.Create(false);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 toto.Terminate;
 toto.WaitFor;
 bmpglobal.Free;
end;


procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
 paintbox1.Canvas.Draw(0,0,bmpglobal);
 caption:=inttostr(gettickcount);
end;

end.

16 réponses

Messages postés
3792
Date d'inscription
samedi 22 décembre 2007
Statut
Membre
Dernière intervention
3 juin 2016
9
Tu peux pas accéder aux objets de la VCL en mode thread, il faut que tu synchronises. Crée une procédure "CentralControl" avec les traitements dedans, et appelle là depuis Execute avec "Synchronize(CentralControl)".

Enfin c'est à ça que je penserais en premier ?

Cordialement, Bacterius !
Messages postés
4720
Date d'inscription
dimanche 26 février 2006
Statut
Modérateur
Dernière intervention
31 juillet 2021
14
bonsoir Barbichette de retour au pays,

j'ai rajouté cette bidouille pour voir quelque chose :

procedure TToto.synchro;
begin
 bmp.LoadFromFile('aurevoir.bmp');
 form1.bmpglobal.canvas.Draw(0,0,bmp);
end;


et apparemment ça fonctionne..

pourquoi ne pas utiliser scanline ?

cantador
Messages postés
220
Date d'inscription
lundi 30 octobre 2000
Statut
Membre
Dernière intervention
15 juillet 2013

Merci Bacterius et Cantador.
Je viens de faire un essai. En effet, ça marche bien avec le chargement d'un bitmap, mais c'est pas vraiment ça que je veux faire..
Pour le scanline, bien sur que je fais un scanline, mais c'était pour l'exemple (et pour que le traitement soit volontairement long).

Bacterius : Ma procedure synchro est déjà appelé avec la methode synchronize.
Mettre à jour le titre de la fenêtre ou un tlabel, aucun souci. J'arrive encore à faire ça.
Mais je ne comprend pas pourquoi mon bitmap local au thread, une fois dessiné, ne s'affiche pas dans le paintbox.

J'ai fais deux petites choses :
1) j'ai retiré l'appel à paintbox1.repaint et je dessin direct dans le canvas. Pour ne pas encombré la file d'attente des messages.
2) en faisant l'essai de mettre la boucle de traitement (le remplissage bourrin des pixels) dans la procédure synchronisée, ça marche nickel... Mais ça bloque le programme principal. Logique...

Bref, j'ai l'impression que les bitmaps et les threads, dans Delphi, c'est pas fait pour marcher ensemble...

Barbichette
Messages postés
3792
Date d'inscription
samedi 22 décembre 2007
Statut
Membre
Dernière intervention
3 juin 2016
9
Bref, j'ai l'impression que les bitmaps et les threads, dans Delphi, c'est pas fait pour marcher ensemble...

Correction : Le bitmap et l'humain ne sont pas faits pour travailler ensemble.

Cordialement, Bacterius !
Messages postés
2527
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
16 octobre 2019
18
Salut,

Peut-être voir la méthode Lock de TCanvas (TCanvas est l'un des rares objets prévus pour la synchronisation).
Messages postés
3826
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
10 mai 2021
46
Salut,


ça ne fonctionne pas tout simplement parce que le Bitmap de Form1 n'est pas mis à jour après modification de BMP ... !!!
Mais en dehors de ça créer un Bitmap dans la procédure Execute d'un thread c'est pas une bonne idée .. si vraiment tu as besoin d'un bitmap crée un constructeur et surcharge le destructeur pour créer et détruire le Bitmap ... mais une seule fois !!!

sinon voici un bout de code fonctionnel:
[hr]unit Unit1;


[b]interface

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

[b]type

  /bTToto =   class (tthread)
  [b]private
    procedure /bsynchro;
  [b]protected
    procedure /bExecute; override;
  end;



  TForm1  = class(TForm)
    PaintBox1: TPaintBox;
    procedure PaintBox1Paint(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  [b]private
    /b { Déclarations privées }
  [b]public
    /b{ Déclarations publiques }
    bmpglobal: tbitmap;
  end;

[b]var
  /bForm1: TForm1;
  toto: TToto;

[b]implementation

/b{$R *.dfm}

{ TToto }

procedure TToto.Execute;
[b]begin
  while not /bTerminated [b]do
    /bsynchronize(synchro);
end;

procedure TToto.synchro;
var i, j, n: integer;
[b]begin
  with /bform1.bmpglobal [b]do
  begin
    /bn : =  random(500);
     for  i : = 0 to Width - 1 [b]do
      for /bj :=  0  to  Height - 1 [b]do
        /bcanvas.Pixels[i, j] : = i * j * n;
  end;
  form1.PaintBox1.Repaint;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
[b]begin
  /bpaintbox1.Canvas.Draw(0, 0, bmpglobal);
  caption :=  inttostr(gettickcount);
end ;

procedure TForm1.FormCreate(Sender: TObject);
[b]begin
  /bDoubleBuffered : = true;
  bmpglobal := TBitmap.Create;
  bmpglobal.Width := paintbox1.Width;
  bmpglobal.height := paintbox1.height;
  toto := TToto.Create(false);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
[b]begin
  /btoto.Terminate;
  toto.WaitFor;
  bmpglobal.Free;
end;

end.
[hr]


[hr]@+Cirec
[hr]
Messages postés
3826
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
10 mai 2021
46
voici la version avec un Bitmap comme buffer:

[hr]unit Unit1;


[b]interface

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

[b]type

  /bTToto =   class (tthread)
  [b]private
    /bbmp: tbitmap;
    procedure synchro;
  [b]protected
    procedure /bExecute; override;
  [b]public
    constructor /bCreate(CreateSuspended: Boolean);
    destructor Destroy; override;
  end;



  TForm1  = class(TForm)
    PaintBox1: TPaintBox;
    procedure PaintBox1Paint(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  [b]private
    /b { Déclarations privées }
  [b]public
    /b{ Déclarations publiques }
    bmpglobal: tbitmap;
  end;

[b]var
  /bForm1: TForm1;
  toto: TToto;

[b]implementation

/b{$R *.dfm}

{ TToto }

constructor TToto.Create(CreateSuspended: Boolean);
[b]begin
  inherited /bCreate(CreateSuspended);
  BMP : =  TBitmap.Create;
end ;

destructor TToto.Destroy;
[b]begin
  /bBMP.Free;
  inherited Destroy;
end;

procedure TToto.Execute;
[b]begin
  while not /bTerminated [b]do
    /bsynchronize(synchro);
end;

procedure TToto.synchro;
var i, j, n: integer;
[b]begin
  /bbmp.width : = form1.bmpglobal.Width;
  bmp.Height :=  form1.bmpglobal.Height;
  n := random(500);
   for  i : = 0 to bmp.Width - 1 [b]do
    for /bj :=  0  to  bmp.Height - 1 [b]do
      /bbmp.canvas.Pixels[i, j] : = i * j * n;
  form1.bmpglobal.Canvas.Draw(0,0,BMP);
  form1.PaintBox1.Repaint;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
[b]begin
  /bpaintbox1.Canvas.Draw(0, 0, bmpglobal);
  caption :=  inttostr(gettickcount);
end ;

procedure TForm1.FormCreate(Sender: TObject);
[b]begin
  /bDoubleBuffered : = true;
  bmpglobal := TBitmap.Create;
  bmpglobal.Width := paintbox1.Width;
  bmpglobal.height := paintbox1.height;
  toto := TToto.Create(false);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
[b]begin
  /btoto.Terminate;
  toto.WaitFor;
  bmpglobal.Free;
end;

end.
[hr]


[hr]@+Cirec
[hr]
Messages postés
220
Date d'inscription
lundi 30 octobre 2000
Statut
Membre
Dernière intervention
15 juillet 2013

Bon, j'ai trouvé la solution.
Il faut encadrer la modification du bitmap local au thread d'un Lock et Unlock...
Sinon, il ne dessine pas vraiment dans le canvas et l'image final est bien un rectangle blanc, vide.

Je vais chercher une utilisation simple et mettre une source en ligne

Merci les pro...

Barbichette
Messages postés
2527
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
16 octobre 2019
18
... Fais bien attention à ce que tu mets entre Lock et Unlock !
Cela ne doit concerner QUE du dessin effectif sur le Canvas (essaie de mettre les précalculs en dehors par exemple). Sinon ça risque de ralentir l'application.

Ceci dit, Lock/Unlock n'est pas obligatoire et un synchronize comme le montre Cirec est tout aussi efficace, je pense.
Messages postés
220
Date d'inscription
lundi 30 octobre 2000
Statut
Membre
Dernière intervention
15 juillet 2013

Je m'explique un peu mieux.
j'ai un thread avec un bitmap local (bmplocal), sur lequel je fais un traitement long.

J'ai par ailleurs un paintbox sur une fenêtre dans lequel je veux faire apparaitre le résultat du traitement.

L'utilisateur peu modifier les paramètres de traitement en direct mais le rendu n'est pas instantané.

Pour ce faire, j'ai un bitmap temporaire bmptmp dans lequel je colle le résultat. Ainsi, à chaque évènement OnPaint, je suis capable de redessiner le dernier résultat de mon thread.

A la fin de mon traitement, dans mon thread, je copie le résultat de bmplocal dans bmptmp, et je met à jour le paintbox.

Le problème était qu'il n'y avait rien dans bmplocal.
Pour résoudre le problème, je dois mettre un bmplocal.Lock et bmplocal.unlock autour de ma boucle de traitement, sinon, pas d'image dans bmplocal.
Il ne bloque rien puisque c'est un bitmap local au thread.

Je ne sais pas si c'est pareil chez les autres...

Enfin, je fais faire un bout de code pour être plus clair..


Barbichette
Messages postés
2527
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
16 octobre 2019
18
Ok, je vois.

Jusqu'à présent, dans ce cas, moi j'initialisais le bmplocal en blanc, par exemple... Mais ce n'est sans doute pas la meilleure solution.
Messages postés
3826
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
10 mai 2021
46
si en plus personne ne lit mes réponses

non c'est faux ... tu n'avais rien à l'écran tout simplement parce que tu dessines sur le Bitmap B et tu affiches le Bitmap A qui lui n'est jamais mis à jour ...
donc forcément le résultat est blanc !!!!


lit et regarde et essaye le code (le deuxième) c'est exactement tes variables et le résultat est présent à l'écran .... pas besoin de Canvas.Lock dans ce cas de figure !!!
et attention à ta procedure Execute ... il doit y avoir un minimum et aucun cas du code faisant appel à des composants qui doivent être dessinés à l'écran ...

le reste est dans le code

[hr]@+Cirec
[hr]
Messages postés
4720
Date d'inscription
dimanche 26 février 2006
Statut
Modérateur
Dernière intervention
31 juillet 2021
14
J'ai testé le 2E code à cirec, et il fonctionne très bien..

Le résutat une image dont les pixels se modifient
donnant une impression d'un écran mal réglé et avec en primes quelques saccades

mais c'est peut-être l'effet désiré par
Barbichette..


cantador
Messages postés
220
Date d'inscription
lundi 30 octobre 2000
Statut
Membre
Dernière intervention
15 juillet 2013

cirec: j'ai bien lu ton code. Le problème, c'est que le traitement (remplissage à la c...) est dans la procédure synchro. Donc pendant tout le temps de traitement, l'application se fige. Donc, aucun intérêt de faire un thread.

De plus, et je ne sais pas pourquoi, moi aussi ça marche quand je remplis le bitmap buffer dans la procédure synchro, mais ça marche plus quand je la sors de cette procédure et que je la recolle dans le 'Execute'. Pourtant, pour le traitement, il n'y a aucun appel à un objet de ma fenêtre principale, mais juste du remplissage d'un bitmap local.
Et dans ce dernier cas, il remplis rien du tout alors qu'en entourant le traitement d'un lock, unlock, ça marche.

Je posterai le code que j'ai fait, avec ce que je veux faire, et on pourra en rediscuter après le code.

Barbichette
Messages postés
3826
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
10 mai 2021
46
ouai parce que là j'avoue que c'est pas très claire pour moi


[hr]@+Cirec
[hr]
Messages postés
2527
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
16 octobre 2019
18
Pour moi non plus, c'est pas clair c't'affaire... :)
Mais, dans un sens, j'ai rien pu tester non plus :s

En principe, un Lock/Unlock c'est fait pour du multi-threading. Càd quand plusieurs threads travaillent sur le même canvas.
Sinon, je ne vois pas l'intérêt de locker un Canvas qui ne risque pas d'être modifié par un autre thread.

On va peut-être découvrir un nouveau 'truc', là...
Vas-y barbichette !
Pour une fois, je suis impatient.