Difference between revisions of "Multithreaded Application Tutorial/fr"

From Free Pascal wiki
Jump to navigationJump to search
Line 68: Line 68:
  
 
Exemple :
 
Exemple :
 
+
<delphi>
 
   Type
 
   Type
 
     TMyThread = class(TThread)
 
     TMyThread = class(TThread)
Line 79: Line 79:
 
       Constructor Create(CreateSuspended : boolean);
 
       Constructor Create(CreateSuspended : boolean);
 
     end;
 
     end;
 
+
</delphi>
 +
<delphi>
 
   constructor TMyThread.Create(CreateSuspended : boolean);
 
   constructor TMyThread.Create(CreateSuspended : boolean);
 
   begin
 
   begin
Line 85: Line 86:
 
     inherited Create(CreateSuspended);
 
     inherited Create(CreateSuspended);
 
   end;
 
   end;
 
+
</delphi>
 +
<delphi>
 
   procedure TMyThread.ShowStatus;
 
   procedure TMyThread.ShowStatus;
 
   // this method is executed by the mainthread and can therefore access all GUI elements.
 
   // this method is executed by the mainthread and can therefore access all GUI elements.
Line 91: Line 93:
 
     Form1.Caption := fStatusText;
 
     Form1.Caption := fStatusText;
 
   end;
 
   end;
+
</delphi>
 +
<delphi>
 
   procedure TMyThread.Execute;
 
   procedure TMyThread.Execute;
 
   var
 
   var
Line 111: Line 114:
 
       end;
 
       end;
 
   end;
 
   end;
 
+
</delphi>
 
Sur l'application ,
 
Sur l'application ,
 
+
<delphi>
 
   var
 
   var
 
     MyThread : TMyThread;
 
     MyThread : TMyThread;
Line 123: Line 126:
 
     MyThread.Resume;
 
     MyThread.Resume;
 
   end;
 
   end;
 
+
</delphi>
 
Si vous voulez rendre votre demande plus flexible vous pouvez créer un événement pour la tâche  - de cette façon votre méthode synchronized ne sera pas étroitement couplée  avec une fiche spécifique ou une classe - you can attach listeners to the thread's event. Voici un exemple :
 
Si vous voulez rendre votre demande plus flexible vous pouvez créer un événement pour la tâche  - de cette façon votre méthode synchronized ne sera pas étroitement couplée  avec une fiche spécifique ou une classe - you can attach listeners to the thread's event. Voici un exemple :
  
 
+
<delphi>
 
   Type
 
   Type
 
     TShowStatusEvent = procedure(Status: String) of Object;
 
     TShowStatusEvent = procedure(Status: String) of Object;
Line 141: Line 144:
 
       property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;
 
       property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;
 
     end;
 
     end;
 
+
</delphi>
 +
<delphi>
 
   constructor TMyThread.Create(CreateSuspended : boolean);
 
   constructor TMyThread.Create(CreateSuspended : boolean);
 
   begin
 
   begin
Line 147: Line 151:
 
     inherited Create(CreateSuspended);
 
     inherited Create(CreateSuspended);
 
   end;
 
   end;
 
+
</delphi>
 +
<delphi>
 
   procedure TMyThread.ShowStatus;
 
   procedure TMyThread.ShowStatus;
 
   // this method is executed by the mainthread and can therefore access all GUI elements.
 
   // this method is executed by the mainthread and can therefore access all GUI elements.
Line 156: Line 161:
 
     end;
 
     end;
 
   end;
 
   end;
 
+
</delphi>
 +
<delphi>
 
   procedure TMyThread.Execute;
 
   procedure TMyThread.Execute;
 
   var
 
   var
Line 176: Line 182:
 
       end;
 
       end;
 
   end;
 
   end;
 
+
</delphi>
 
Sur l'application ,
 
Sur l'application ,
 
+
<delphi>
 
   Type
 
   Type
 
     TForm1 = class(TForm)
 
     TForm1 = class(TForm)
Line 192: Line 198:
 
       { public declarations }
 
       { public declarations }
 
     end;
 
     end;
 
+
</delphi>
 +
<delphi>
 
   procedure TForm1.FormCreate(Sender: TObject);
 
   procedure TForm1.FormCreate(Sender: TObject);
 
   begin
 
   begin
Line 199: Line 206:
 
     MyThread.OnShowStatus := @ShowStatus;
 
     MyThread.OnShowStatus := @ShowStatus;
 
   end;
 
   end;
 
+
</delphi>
 +
<delphi>
 
   procedure TForm1.FormDestroy(Sender: TObject);
 
   procedure TForm1.FormDestroy(Sender: TObject);
 
   begin
 
   begin
Line 206: Line 214:
 
     inherited;
 
     inherited;
 
   end;
 
   end;
 
+
</delphi>
 +
<delphi>
 
   procedure TForm1.Button1Click(Sender: TObject);
 
   procedure TForm1.Button1Click(Sender: TObject);
 
   begin
 
   begin
 
   MyThread.Resume;
 
   MyThread.Resume;
 
   end;
 
   end;
 
+
</delphi>
 +
<delphi>
 
   procedure TForm1.ShowStatus(Status: string);
 
   procedure TForm1.ShowStatus(Status: string);
 
   begin
 
   begin
 
     Label1.Caption := Status;
 
     Label1.Caption := Status;
 
   end;
 
   end;
 
+
</delphi>
 
= Choses spéciales dont il faut s'occuper  =
 
= Choses spéciales dont il faut s'occuper  =
 
Il y a une prise de tête potentielle dans Windows avec les Threads si vous employez l'option -Ct (contrôle de pile ).
 
Il y a une prise de tête potentielle dans Windows avec les Threads si vous employez l'option -Ct (contrôle de pile ).
Line 224: Line 234:
 
Un bon code à vérifier pour ça et pour d'autres exceptions qui peuvent se produirent dans la création d'une tâche est:
 
Un bon code à vérifier pour ça et pour d'autres exceptions qui peuvent se produirent dans la création d'une tâche est:
  
 
+
<delphi>
 
     MyThread:=TThread.Create(False);
 
     MyThread:=TThread.Create(False);
 
     if Assigned(MyThread.FatalException) then
 
     if Assigned(MyThread.FatalException) then
 
       raise MyThread.FatalException;
 
       raise MyThread.FatalException;
 
+
</delphi>
  
 
Ce code asurera que toute exception qui se produit pendant la création d'une tâche sera relevée(et traitée) dans votre tâche principale .
 
Ce code asurera que toute exception qui se produit pendant la création d'une tâche sera relevée(et traitée) dans votre tâche principale .
Line 237: Line 247:
  
 
Ainsi, votre code d'application Lazarus devrait resssembler à :
 
Ainsi, votre code d'application Lazarus devrait resssembler à :
 
+
<delphi>
 
   program MyMultiThreadedProgram;
 
   program MyMultiThreadedProgram;
 
   {$mode objfpc}{$H+}
 
   {$mode objfpc}{$H+}
Line 247: Line 257:
 
     Forms
 
     Forms
 
     { add your units here },
 
     { add your units here },
 
+
</delphi>
 
Si vous oubliez ceci vous obtiendrez cette erreur au démarrage :
 
Si vous oubliez ceci vous obtiendrez cette erreur au démarrage :
 
   Le fichier binaire n'a aucun support de tâches compilé en lui.
 
   Le fichier binaire n'a aucun support de tâches compilé en lui.
Line 265: Line 275:
  
 
L'unité LCLProc contient plusieurs fonctions , pour laisser chaque tâche écrire à son propre fichier journal:
 
L'unité LCLProc contient plusieurs fonctions , pour laisser chaque tâche écrire à son propre fichier journal:
 +
<delphi>
 
   procedure DbgOutThreadLog(const Msg: string); overload;
 
   procedure DbgOutThreadLog(const Msg: string); overload;
 
   procedure DebuglnThreadLog(const Msg: string); overload;
 
   procedure DebuglnThreadLog(const Msg: string); overload;
 
   procedure DebuglnThreadLog(Args: array of const); overload;
 
   procedure DebuglnThreadLog(Args: array of const); overload;
 
   procedure DebuglnThreadLog; overload;
 
   procedure DebuglnThreadLog; overload;
 
+
</delphi>
 
Par exemple :
 
Par exemple :
 
Au lieu de  ''writeln('Some text ',123);'' utilisez   
 
Au lieu de  ''writeln('Some text ',123);'' utilisez   
Line 325: Line 336:
  
 
Déclarer la section (de manière globale pour toutes les tâches qui devraient accéder à la section):
 
Déclarer la section (de manière globale pour toutes les tâches qui devraient accéder à la section):
 +
<delphi>
 
   MyCriticalSection: TRTLCriticalSection;
 
   MyCriticalSection: TRTLCriticalSection;
 
+
</delphi>
 
Créer la section :
 
Créer la section :
 +
<delphi>
 
   InitializeCriticalSection(MyCriticalSection);
 
   InitializeCriticalSection(MyCriticalSection);
 
+
</delphi>
 
Executer quelques tâches. En faisant quelque chose exclusivement  
 
Executer quelques tâches. En faisant quelque chose exclusivement  
 +
<delphi>
 
   EnterCriticalSection(MyCriticalSection);
 
   EnterCriticalSection(MyCriticalSection);
 
   try
 
   try
Line 337: Line 351:
 
     LeaveCriticalSection(MyCriticalSection);
 
     LeaveCriticalSection(MyCriticalSection);
 
   end;
 
   end;
 
+
</delphi>
 
Après que toutes les tâches soient terminées , libérer la section critique :
 
Après que toutes les tâches soient terminées , libérer la section critique :
 +
<delphi>
 
   DeleteCriticalSection(MyCriticalSection);
 
   DeleteCriticalSection(MyCriticalSection);
 
+
</delphi>
 
Comme alternative, vous pouvez employer un objet TCriticalSection. La création fait l'initialisation, la méthode Enter fait pénétrer dans EnterCriticalSection, la méthode Leave fait LeaveCriticalSection et la destruction de l'object fait la suppression.
 
Comme alternative, vous pouvez employer un objet TCriticalSection. La création fait l'initialisation, la méthode Enter fait pénétrer dans EnterCriticalSection, la méthode Leave fait LeaveCriticalSection et la destruction de l'object fait la suppression.
  
Line 365: Line 380:
 
Pour un exemple voir: lazarus/examples/multithreading/waitforexample1.lpi
 
Pour un exemple voir: lazarus/examples/multithreading/waitforexample1.lpi
  
<pre>
+
<delphi>
 
{ TThreadA }
 
{ TThreadA }
  
Line 395: Line 410:
 
   end;
 
   end;
 
end;
 
end;
</pre>
+
</delphi>

Revision as of 19:44, 5 July 2007

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

Vue d'ensemble

Cette page essayera d'expliquer comment écrire et corriger une application multi-tâches avec Free Pascal et Lazarus.

Une application multi-tâches est celle qui crée deux tâches d'execution ou plus en même temps.

Si vous êtes un nouveau dans le multi-tâches,veuillez lire le paragraphe "Avez vous besoin du multi-tâches ?" pour découvrir , si vous avez vraiment besoin de ça. Vous pouvez vous éviter beaucoup de maux de tête .

Une des tâches légères s'appelle la tâche principale . La tâche principale est celle qui est créé par le logiciel d'exploitation , une fois que notre application commence . La tâche principale doit être la seule tâche qui met à jour les composants qui interfèrent avec l'utilisateur (autrement , l'application peut accrocher ).

L'idée principale est que l'application peut faire quelques traitements en arrière-plan (dans une deuxième tâche) tandis que l'utilisateur peut continuer à travailler (en employant la tâche principale).

Une autre utilisation des tâches est juste pour avoir une application qui répond mieux. Si vous créez une application, et quand l'utilisateur appuie sur un bouton, l'application commence le traitement(un gros travail )... et pendant le traitement , L'écran arrête de répondre, et donne à l'utilisateur la sensation que l'application est morte,ce n'est pas si agréable. Si le gros travail fonctionne dans une deuxième tâche, l'application continue à répondre (presque ) comme si elle était en idle. Dans ce cas-ci c'est une bonne idée, avant de commencer la tâche, pour neutraliser les boutons de la fiche pour éviter à l'utilisateur de commencer plus d'une tâche pour le travail .

Une autre utilisation , est de créer une application serveur qui est capable de répondre à beaucoup de clients en même temps .

Avez vous besoin du multi-tâche?

Si vous êtes un novice avec le multi-tâche et vous voulez seulement rendre votre application plus nerveuse tandis que votre application calcule une grande charge de travail, alors le multi-tâche ne devrait pas être, ce que vous recherchez. Les applications multi-tâches sont toujours plus difficile à debogguer et elles sont souvent beaucoup plus complexes. Et dans beaucoup de cas vous n'avez pas besoin du multi-tâche . Une seule tâche est suffisante. Si vous pouvez fractionner les longues corvées de travail en plusieurs petits morceaux, alors à la place vous devriez employer Application.ProcessMessages. Cette méthode laisse la LCL traiter tous les messages d'attente et ceux de retour . L'idée est de traiter une partie du travail, puis d'appeller Application.ProcessMessages pour voir si l'utilisateur abandonne ou clique quelque part ou l'indicateur de processus est actualisé, ensuite continuer avec la partie suivante du travail, appeller Application.ProcessMessages et ainsi de suite.

Par exemple : Lecture d'un grand fichier et son traitement . Voir examples/multithreading/singlethreadingexample1.lpi.

Le multi-tâche est seulement nécessaire pour

  • le blocage des handles, comme les communications de réseau
  • l'utilisation de plusieurs processeurs à la fois
  • les algorithmes et les appels de bibliothèque, qui ne peuvent pas être fractionné dans de petites pièces.

La classe TThread

L'exemple suivant peut être trouvé dans le répertoire examples/multithreading/.

Pour creer une application multi-tâches, la manière la plus facile est d'employer la classe TThread.

Cette classe permet la création d'une tâche supplémentaire(à côté de la tâche principale) de manière simple .

Normalement, vous devez seulement surcharger 2 méthodes: le constructeur Create, et la méthode Execute.

Dans le constructeur, vous préparerez la tâche à s'executer. Vous fixerez les valeurs initiales des variables ou propriétés que vous avez besoin. Le constructeur original de TThread exige un paramètre appelé Suspended. Comme vous pourriez le prévoir, fixer Suspended = True empêchera la tâche de commençer automatiquement après sa création . Si Suspended = False, la tâche commencera à fonctionner juste aprés sa création . Si la tâche est créé en étant suspendue, alors elle fonctionnera seulement après que la méthode Resume soit appelée.

A partir de FPC version 2.0.1 et au delà, TThread.Create a également un paramètre implicite pour la taille de la pile . Vous pouvez maintenant changer la taille de la pile par défaut de chaque tâche que vous créez si vous en avez besoin. Les appels de procedure récursifs en profondeur dans une tâche sont un bon exemple. Si vous n'indiquez pas le paramètre de la taille de la pile, une taille de pile de l'OS par défaut est employée.

Dans la méthode surchargée Execute vous écrirez le code qui fonctionnera dans la tâche .

La classe TThread a une propriété importante : Terminated : boolean;

Si la tâche a une boucle (et que c'est habituel), la boucle devrait être quittée quand Terminated vaut true (sa valeur est faux par défaut). Ainsi dans chaque cycle, il doit vérifier si Terminated vaut true, et si c'est le cas, doit sortir de la méthode .Execute aussi rapidement que possible, après tout nettoyage nécessaire .

Aussi gardez à l'esprit que la méthode Terminate ne fait rien par défaut: la méthode .Execute doit explicitement mettre en application un support afin de stopper son travail .

Comme nous avons expliqué plus tôt, la tâche ne devrait pas interagir avec les composants visibles. Pour montrer quelque chose à l'utilisateur il doit le faire dans la tâche principale. Pour faire ceci , une méthode de la classe TThread appelée Synchronize existe . Synchronize exige une méthode (qui ne prend aucun paramètre ) comme argument . Quand vous appelez cette méthode au moyen de Synchronize(@MyMethod), l'execution de la tâche sera mis en pause, le code de MyMethod s'executera dans la tâche principale, et ensuite l'execution de la tâche sera repris. Le fonctionnement exact de Synchronize dépend de la plate-forme, mais fondamentalement il fait ceci: Il signale un message sur la file d'attente de message principale et va dormir. Par la suite la tâche principale traite le message et appelle MyMethod. De cette façon MyMethod est appelé hors contexte, ce qui signifie pas pendant un évènement de déplacement de souris vers le bas ou pendant un événement de peinture, mais après . Après que la tâche principale a executé MyMethod, il réveille la tâche en sommeil et exécute le message suivant. La tâche continue alors .

Il y a une autre propriété importante de la classe TThread: FreeOnTerminate. Si cette propriété est true, l'objet tâche est automatiquement libéré quand l'execution de la tâche (méthode .Execute) s'arrête. Autrement l'application devra le libérer manuellement.

Exemple : <delphi>

 Type
   TMyThread = class(TThread)
   private
     fStatusText : string;
     procedure ShowStatus;
   protected
     procedure Execute; override;
   public
     Constructor Create(CreateSuspended : boolean);
   end;

</delphi> <delphi>

 constructor TMyThread.Create(CreateSuspended : boolean);
 begin
   FreeOnTerminate := True;
   inherited Create(CreateSuspended);
 end;

</delphi> <delphi>

 procedure TMyThread.ShowStatus;
 // this method is executed by the mainthread and can therefore access all GUI elements.
 begin
   Form1.Caption := fStatusText;
 end;

</delphi> <delphi>

 procedure TMyThread.Execute;
 var
   newStatus : string;
 begin
   fStatusText := 'TMyThread Starting...';
   Synchronize(@Showstatus);
   fStatusText := 'TMyThread Running...';
   while (not Terminated) and ([any condition required]) do
     begin
       ...
       [here goes the code of the main thread loop]
       ...
       if NewStatus <> fStatusText then
         begin
           fStatusText := newStatus;
           Synchronize(@Showstatus);
         end;
     end;
 end;

</delphi> Sur l'application , <delphi>

 var
   MyThread : TMyThread;
 begin
   MyThread := TMyThread.Create(True); // This way it doesn't start automatically
   ...
   [Here the code initialises anything required before the threads starts executing]
   ...
   MyThread.Resume;
 end;

</delphi> Si vous voulez rendre votre demande plus flexible vous pouvez créer un événement pour la tâche - de cette façon votre méthode synchronized ne sera pas étroitement couplée avec une fiche spécifique ou une classe - you can attach listeners to the thread's event. Voici un exemple :

<delphi>

 Type
   TShowStatusEvent = procedure(Status: String) of Object;
   TMyThread = class(TThread)
   private
     fStatusText : string;
     FOnShowStatus: TShowStatusEvent;
     procedure ShowStatus;
   protected
     procedure Execute; override;
   public
     Constructor Create(CreateSuspended : boolean);
     property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;
   end;

</delphi> <delphi>

 constructor TMyThread.Create(CreateSuspended : boolean);
 begin
   FreeOnTerminate := True;
   inherited Create(CreateSuspended);
 end;

</delphi> <delphi>

 procedure TMyThread.ShowStatus;
 // this method is executed by the mainthread and can therefore access all GUI elements.
 begin
   if Assigned(FOnShowStatus) then
   begin
     FOnShowStatus(fStatusText);
   end;
 end;

</delphi> <delphi>

 procedure TMyThread.Execute;
 var
   newStatus : string;
 begin
   fStatusText := 'TMyThread Starting...';
   Synchronize(@Showstatus);
   fStatusText := 'TMyThread Running...';
   while (not Terminated) and ([any condition required]) do
     begin
       ...
       [here goes the code of the main thread loop]
       ...
       if NewStatus <> fStatusText then
         begin
           fStatusText := newStatus;
           Synchronize(@Showstatus);
         end;
     end;
 end;

</delphi> Sur l'application , <delphi>

 Type
   TForm1 = class(TForm)
     Button1: TButton;
     Label1: TLabel;
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
   private
     { private declarations }
     MyThread: TMyThread; 
     procedure ShowStatus(Status: string);
   public
     { public declarations }
   end;

</delphi> <delphi>

 procedure TForm1.FormCreate(Sender: TObject);
 begin
   inherited;
   MyThread := TMyThread.Create(true);
   MyThread.OnShowStatus := @ShowStatus;
 end;

</delphi> <delphi>

 procedure TForm1.FormDestroy(Sender: TObject);
 begin
   MyThread.Terminate;
   MyThread.Free;
   inherited;
 end;

</delphi> <delphi>

 procedure TForm1.Button1Click(Sender: TObject);
 begin
  MyThread.Resume;
 end;

</delphi> <delphi>

 procedure TForm1.ShowStatus(Status: string);
 begin
   Label1.Caption := Status;
 end;

</delphi>

Choses spéciales dont il faut s'occuper

Il y a une prise de tête potentielle dans Windows avec les Threads si vous employez l'option -Ct (contrôle de pile ). Pour des raisons pas aussi claires le contrôle de pile se "déclenchera" sur quelconque TThread.Create si vous employez la taille de pile par défaut . Le seul contournement(à ce problème) pour le moment est de ne pas utiliser simplement l'option -Ct. Noter qu'il ne cause pas d'exception dans la tâche principale, mais dans celle nouvellement crée. Ceci "semble" comme si la tâche n'avait jamais commencé .

Un bon code à vérifier pour ça et pour d'autres exceptions qui peuvent se produirent dans la création d'une tâche est:

<delphi>

    MyThread:=TThread.Create(False);
    if Assigned(MyThread.FatalException) then
      raise MyThread.FatalException;

</delphi>

Ce code asurera que toute exception qui se produit pendant la création d'une tâche sera relevée(et traitée) dans votre tâche principale .

Les unités nécessaires pour une application multi-tâches

Vous n'avez besoin d'aucune unité spéciale pour ceci pour travailler avec Windows . Cependant avec Linux, MacOSX et FreeBSD, vous avez besoin de l'unité cthreads et elle doit être la première untité utilisée du projet (l'unité du programme , .lpr)!

Ainsi, votre code d'application Lazarus devrait resssembler à : <delphi>

 program MyMultiThreadedProgram;
 {$mode objfpc}{$H+}
 uses
 {$ifdef unix}
   cthreads,
 {$endif}
   Interfaces, // this includes the LCL widgetset
   Forms
   { add your units here },

</delphi> Si vous oubliez ceci vous obtiendrez cette erreur au démarrage :

 Le fichier binaire n'a aucun support de tâches compilé en lui.
 Recompiler l'application avec un gestionnaire de thread dans la clause uses du programme avant d'autres unités utilisant
 les tâches. (Pour le message en anglais voir version anglaise de cette page)

Support SMP

La bonne nouvelle est que votre application travaille en multi-tâches correctement de cette façon, elle est déjà prête pour le SMP!

Deboguer les application multi-tâches avec Lazarus

Le deboguage sur Lazarus n'est pas encore entièrement fonctionnel.

Deboguer la sortie

Dans une application a une seule tâche, vous pouvez simplement écrire sur la console/le terminal/n'importe quoi et l'ordre des lignes est le même avec lesquelles elles ont été écrites . Dans une application multi-tâche les choses sont plus compliquées. Si deux tâches écrivent, c'est à dire qu'une ligne est écrite par la tâche A avant une ligne par la tâche B, alors les lignes ne sont pas neccessairement écrites dans cet ordre. Il peut même se produire, que une tâche écrit sa propre sortie, tandis que l'autre tâche écrit une ligne .

L'unité LCLProc contient plusieurs fonctions , pour laisser chaque tâche écrire à son propre fichier journal: <delphi>

 procedure DbgOutThreadLog(const Msg: string); overload;
 procedure DebuglnThreadLog(const Msg: string); overload;
 procedure DebuglnThreadLog(Args: array of const); overload;
 procedure DebuglnThreadLog; overload;

</delphi> Par exemple : Au lieu de writeln('Some text ',123); utilisez

 DebuglnThreadLog(['Some text ',123]);

Ceci ajoutera une ligne 'Some text 123' à Log<PID>.txt, où <PID> est l'ID du processus de la tâche courante.

C'est une bonne idée d'enlever le fichier journal avant chaque exécution :

 rm -f Log* && ./project1

Linux

Si vous essayez de deboguer une application multi-tâches sous Linux, vous aurez un grand problème : le serveur X va crasher !

On ne sait pas comment résoudre cela proprement, mais un contournement de ce problème est:

Créer une nouvelle instance de X avec :

 X :1 &

Il s'ouvrira, et quand vous commuterez vers un autre bureau (celui avec lequel vous travaillez en appuyant sur CTRL+ALT+F7), vous pourrez retourner vers le nouveau bureau graphique avec CTRL+ALT+F8 (si cette combinaison ne fonctionne pas , essayez avec CTRL+ALT+F2... celui-ci a fonctionné avec Slackware).

Alors vous pourriez , si vous voulez , créer une session bureau au démarrage de X avec:

 gnome-session --display=:1 &

Puis, dans Lazarus, avec la boite de dialogue "paramètres d'exécution..." du menu Exécuter, cocher "Utiliser l'affichage" et entrez :1.

Maintenant l'application s'exécutera sur le deuxième serveur X et vous serez capable de la déboguer avec le premier.

Ceci a été testé avec Free Pascal 2.0 et Lazarus 0.9.10 sur Windows et Linux.



Au lieu de créer une nouvelle session X, on peut employer Xnest. Xnest est une session X session sur une fenêtre . En l'utilisant, le serveur X n'est pas bloqué pendant le debogage des processus, et c'est beaucoup plus facile de deboguer sans être tenu de changer de terminals.

La ligne de commande pour exécuter Xnest est

 Xnest :1 -ac

pour créer une session X sur :1, et interdire de contrôle d'accès.

Widgetsets

Les interfaces win32, gtk et carbon supportent entièrement le multi-tâches. Ceci signifie que, TThread, les sections critiques et Synchronize fonctionnent.

Les sections critiques

Une section critique est un objet employé pour s'assurer, qu'une certaine partie du code est exécutée seulement par une tâche à la fois. Une section critique doit être créée /initialisée avant qu'elle puisse être employée et être libérée quand elle n'est plus nécessaire.

Les sections critiques sont normalement employées de cette façon :

Ajouter l'unité SyncObjs.

Déclarer la section (de manière globale pour toutes les tâches qui devraient accéder à la section): <delphi>

 MyCriticalSection: TRTLCriticalSection;

</delphi> Créer la section : <delphi>

 InitializeCriticalSection(MyCriticalSection);

</delphi> Executer quelques tâches. En faisant quelque chose exclusivement <delphi>

 EnterCriticalSection(MyCriticalSection);
 try
   // accéder à quelques variables , écrire les fichiers , envoyer quelques paquets de réseau , etc
 finally
   LeaveCriticalSection(MyCriticalSection);
 end;

</delphi> Après que toutes les tâches soient terminées , libérer la section critique : <delphi>

 DeleteCriticalSection(MyCriticalSection);

</delphi> Comme alternative, vous pouvez employer un objet TCriticalSection. La création fait l'initialisation, la méthode Enter fait pénétrer dans EnterCriticalSection, la méthode Leave fait LeaveCriticalSection et la destruction de l'object fait la suppression.

Par exemple : 5 tâches incrémentant un compteur. Voir lazarus/examples/multithreading/criticalsectionexample1.lpi

PRENDRE GARDE : Il y a deux jeux pour les 4 fonctions ci-dessus. Celui de la bibliothèque RTL et celui de la LCL. Celui de la LCL est défini dans les unités LCLIntf et LCLType. Tous les deux fonctionnent plus ou moins de la même façon . Vous pouvez les employer tous les deux en même temps dans votre application , mais vous ne devriez pas employer une fonction de la bibliothèque RTL avec une section critique LCL et vice-versa.


Partage de variables

Si certaines tâches partagent une variable, seulement en lecture, alors il n'y a pas à s'inquiéter. juste la lire. Mais si une ou plusieurs tâches modifient la variable, alors vous devez vous assurer , que seulement une tâche accède aux variables à la fois .

Par exemple : 5 tâches incrémentant un compteur. Voir lazarus/examples/multithreading/criticalsectionexample1.lpi

Attente une autre tâche

Si une tâche A a besoin du résultat d'une autre tâche B, elle doit attendre , jusqu'à ce que B ait fini .

Important: La tâche principale ne devrait jamais attendre pour une autre tâche. Au lieu de cela utilisez Synchronize (voir ci-dessus).

Pour un exemple voir: lazarus/examples/multithreading/waitforexample1.lpi

<delphi> { TThreadA }

procedure TThreadA.Execute; begin

 Form1.ThreadB:=TThreadB.Create(false);
 // create event
 WaitForB:=RTLEventCreate;
 while not Application.Terminated do begin
   // wait infinitely (until B wakes A)
   RtlEventWaitFor(WaitForB);
   writeln('A: ThreadB.Counter='+IntToStr(Form1.ThreadB.Counter));
 end;

end;

{ TThreadB }

procedure TThreadB.Execute; var

 i: Integer;

begin

 Counter:=0;
 while not Application.Terminated do begin
   // B: Working ...
   Sleep(1500);
   inc(Counter);
   // wake A
   RtlEventSetEvent(Form1.ThreadA.WaitForB);
 end;

end; </delphi>