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();
}
|