vendredi 11 avril 2003

Thread


THREAD

Le parallélisme fait partie de notre quotidien. Il n'est pas rare d'exécuter plusieurs actions en même temps : manger, écouter, écrire... Une telle approche peut-être utilisé en programmation. De plus en plus de logiciels utilisent cette technique. Cette méthode est employée par certains lecteurs vidéo et audio sur le Web. On peut ainsi écouter le fichier sans devoir attendre qu'il soit totalement téléchargé. Un thread exécute le fichier, un autre le télécharge.

Le système alloue un certain temps d'exécution à chaque tâche. Le temps alloué à chaque programme est si court qu'on a l'impression que tout fonctionne simultanément. Un tel système est dit multitâche de prémption.

Un thread est un sous-programme d'une application, c'est une unité d'exécution de celui-ci. Plusieurs threads peuvent fonctionner en même temps dans une application. L'utilisation des threads permet d'augmenter les performances.

Priorité

La priorité permet d'allouer un certain temps d'exécution aux threads. Les valeurs suivantes peuvent être affectés à la propriété «Priority» du thread.
  • tpIdle Le thread s'exécute seulement quand le système est en attente.
  • tpLowest La priorité du thread est deux points en dessous de la normale.
  • tpLower La priorité du thread est un point en dessous de la normale.
  • tpNormal La priorité du thread est normale.
  • tpHigher La priorité du thread est un point au-dessus de la normale
  • tpHighest La priorité du thread est deux points au-dessus de la normale.
  • tpTimeCritical Le thread a la priorité la plus élevée.
Il faut bien identifier les besoins qu'on a pour ne pas allouer trop de temps à un thread qui attend des donnés de l'extérieur, on réduisait ainsi les performances au lieu de les augmenter.

Synchronisation de base

Il a sa propre pile et les variables locales sont locales à chaque thread. Des données globabes peuvent être partagées entre les différents threads, il peut donc avoir des problèmes de synchronisation. S'il y a qu'un thread qui utilise une variable globale, il n'y a aucun problème.

Aperçu des méthodes

Création

Il faut créer un descendant de la classe TThread

Execute

Cette méthode est abstraite, elle contiendra le code à exécuter lorsque le thread débutera.

Terminated

Cette propriété et la méthode Terminate permettent d'arrêter un thread.

Suspended

En affectant true à cette propriété ou en appelant la méthode Suspend, le thread sera suspendu.

Resume

Cette méthode reprend l'exécution d'un thread interrompu.

Synchronize

Cette méthode sert à exécuter une méthode dans le thread principal de la vcl. On évite de nombreux conflits lors du multi-thread en l'employant. La synchronisation peut s'avérer nécessaire lorsqu'on utilise des objets de la vcl/clx.
L'unité classes doit être incluse dans la partie uses.

Ce thread servira à afficher le nombre de fois qu'il est passé dans une boucle.

type
  TLoop = class(TThread)
  private
    cptIteration : integer;
  protected
    constructor create();
    procedure Execute; override;
    procedure ReAfficher;
end;
Nous sommes obligés de définir la méthode execute.
procedure TLoop.Execute;
begin
  while (not Terminated) do begin
    inc(cptIteration);
     Synchronize(ReAfficher);
  end;
end;

Destruction de thread

Afin d'évider les problèmes lors de la destruction de threads, il faut quitter le threads convenablement, récupérer la mémoire allouée et obtenir les résultats du threads.
Si le threads ne doit pas communiquer de l'information à nouveau au thread principal de la vcl, on n'a pas besoin de se préoccuper réllement de sa libération de mémoire. On utilise FreeOnTerminate et le thread effectue lui-même sa propre désalocation de la mémoire.

Arrêt prématuré de thread

Il peut arriver qu'un thread doit en informer un autre qu'il va se terminer. Ceci se produit généralement si un thread exécute une opération prolongé et que l'utilisateur décide de quitter l'application ou que l'exécution doit être interrompue. TThread inclut la méthode Terminate et la propriété Terminated pour supporter de telles situations. Lorsque thread est démaré, sa propriété terminated est mise à false.C'est la responsabilité des threads de vérifier périodiquement s'ils ont été terminés.En somme cette propriété signifié terminait le thread le plus rapidement possible.

L'événement OnTerminate

Cet évènement survient lorsqu'un thread a fini d'être exécuté et non lorsque la méthode Terminate est appelée. C'est très utile car on l'exécute comme le thread principal de la vcl. C'est une façon très intéressante de ne pas avoir un thread vcl et de transférer ses donnés à un thread de vcl. OnTerminate fonctionne étrangement à synchronise.

Grâce à du code trouvé sur le Web, j'ai créé une petite application qui peut exécuter jusqu'à 3 threads en même temps. Ce programme vous permettra de mieux comprendre les notions apprises sur les threads jusqu'à maintenant.
Code source disponible ici

Blocage

Méthode de WaitFor

Il peut arriver qu'on est besoin que tous les autres thread soient terminés à un certain moment lorsqu'on fait exécuter le thread de la vcl. voici un petit exemple pour mieux comprendre ce que fait cette méthode.

Un thread A appelle la méthode WaitFor d'un thread B, le tread A est mis en attente jusqu'à ce que le thread B est fini son exécution. Lorsque le thread A reprend, il est certain que le résultat du thread B peut-être lu et que l'objet du thread représentant B peut être détruit.

Il faut faire attention lorsqu'on utilise cette méthode, car elle ne peut pas recevoir de message. Le formulaire ne pourra pas se redessiner, se redimensionner et communiquer avec l'extérieur. En appelant PostMessage le problème sera moins visible, mais si le traitement est vraiment long, il faudrait penser à ajouter un messager à l'usager. Il existe de nombreuses techniques pour résoudre ce problème dont les messages d'attente de win32.

Danger

Si on utilise synchronize et waitfor il est possible qu'il se produise un blocage de notre application. Un blocage survient si les threads s'attendent les uns les autres de façon cyclique. Un cercle vicieux en quelques sortes. Ce problème peut-être résout en levant des exceptions. En utilisant pas synchronise et waitfor dans la même application, on s'assure ainsi de ne pas rencontrer ce problème.

Thread: avancé

Inconvénient de synchronize

Synchronise a plusieurs inconvénients qui peuvent le rendre inacceptable pour des applications multi-thread complexe.
  • Utile lorsqu'on veut faire communiquer nos threads avec ceux de la vcl
  • Si on utilise beaucoup de synchronize, la vcl bloque notre application
  • Synchronize peut bloquer notre application si la vcl attend nos threads
Le thread principal de l'application devrait s'exécuter rapidement pour avoir un temps réponse optimal.

Il arrive très souvent que des threads communiquent avec la vcl dans le même sens. C'est à dire lire un flux de donnés, exécuter une requête... Des données sont ainsi partagées. Il faut alors s'assurer que seul un thread pourra modifier des donnés. La classe TCriticalSection est une bonne solution.

Section critique

La classe TCriticalSection permet de bloquer l'accès à une partie du code à plusieurs threads. Seul un thread pourra exécuter le code. La méthode Acquire empêche les autres threads d'exécuter une section et Release enlève cette protection. L'api de Windows a bien évidemment des fonctionnalités plus complètes.