C#Threads

Ceci est un exemple implémentant deux exemples de threading en C#. Vous pourrez comparer par vous-mêmes avec des programmes équivalents dans les langages que vous connaissez.

Pour représenter un signal d'arrêt, nous utiliserons une petite classe Signal maison, encapsulant un signal booléen (pas ce qu'il y a de plus souple, soit, mais ça fera le boulot pour le moment).

Notez le recours à une propriété bool encapsulant un attribut volatile bool. En effet :

  • En C#, au moment d'écrire ceci, il n'est pas permis d'écrire une propriété volatile
  • Le recours au mot volatile ici est nécessaire pour que le compilateur soit informé du besoin de faire en sorte que les accès sur le booléen soient synchronisés. En effet, un accès concurrent par au moins deux fils d'exécution sur un même objet (pris au sens large du terme, et incluant les primitifs) sans synchronisation entraîne une Data Race, et ces bogues sont extrêmement méchants
  • Il est toutefois possible d'enrober un attribut volatile bool (donc ayant les bonnes propriétés pour ce qui est de la synchronisation) dans une propriété bool
using System;
using System.Threading;

// note : le code du programme principal (plus bas) va ici

class Signal
{
   private volatile bool reçu;
   public bool Reçu
   {
      get { return reçu; }
      set { reçu = value; }
   }
}

Une classe TiPoints représentera, avec sa méthode d'instance Exécuter(), l'acte d'afficher un petit point à la console jusqu'à ce qu'un signal d'arrêt lui soit envoyé.

class TiPoints
{
   private Signal signal;
   public TiPoints(Signal signal)
   {
      this.signal = signal ?? throw new ArgumentException();
   }
   public void Exécuter()
   {
      while (!signal.Reçu)
      {
         Console.Write(".");
         Thread.Sleep(1000);
      }
   }
}

Un thread en C# est un délégué sur une méthode void ne recevant pas de paramètre. Dans le programme principal, nous en voyons deux exemples simples :

  • La méthode ExempleA() utilise la méthode d'instance Exécuter() d'un TiPoints à titre de thread, et lit une touche dans le code ayant lancé ce thread, puis attend sa complétion (invocation de Join(), comme dans le monde POSIX), et
  • La méthode ExempleB() utilise la méthode de classe LireTouche() à titre de thread, ce qui impose (dans ce cas-ci) le recours à un Signal accessible à une telle méthode

Concrètement, on choisira l'approche qui nous convient.

// programme principal (insérer en haut des classes)
Console.WriteLine("Exemple avec délégué sur une méthode d'instance");
ExempleA();
Console.WriteLine("Exemple avec délégué sur une méthode de classe");
ExempleB();

static void ExempleA()
{
   Signal signal = new Signal();
   var th = new Thread(new TiPoints(signal).Exécuter);
   th.Start();
   Console.ReadKey();
   signal.Reçu = true;
   th.Join();
}
static Signal g_signal = new Signal();
static void LireTouche()
{
   Console.ReadKey();
   g_signal.Reçu = true;
}
static void ExempleB()
{
   TiPoints tp = new (g_signal);
   Thread th = new (LireTouche);
   th.Start();
   tp.Exécuter();
   th.Join();
}

Plus simplement, on peut utiliser une expression λ pour modéliser un fil d'exécution.

Ici, le fil d'exécution accepte une λ de signature appropriée. Cette dernière accède à la variable fini par référence, et fait une partie de la tâche attendue de ce système (la lecture d'une touche).

C'est tout de même plus simple et plus élégant, n'est-ce pas?

using System;
using System.Threading;

bool fini = false;
var th = new Thread(() =>
{
   Console.ReadKey();
   fini = true;
});
th.Start();
while(!fini)
{
   Console.Write(".");
   Thread.Sleep(1000);
}
th.Join();

Valid XHTML 1.0 Transitional

CSS Valide !