Votre exemple est très intéressant - il montre les effets secondaires du traitement parallèle. Pour répondre à votre question, et pour faciliter la visualisation des effets secondaires, j'ai légèrement modifié votre exemple :
void Main()
{
System.Timers.Timer t = new System.Timers.Timer (10);
t.Enabled=true;
t.Elapsed+= (sender, args) =>c();
Console.ReadLine();
}
int t=0; int h=0;
public void c()
{
h++; new Thread(() => doWork(h)).Start();
}
public void doWork(int h2)
{
try
{
t++; string.Format("h={0}, h2={1}, threads={2} [start]",
h, h2, t).Dump();
Thread.Sleep(3000);
}
finally {
t--; string.Format("h={0}, h2={1}, threads={2} [end]",
h, h2, t).Dump();
}
}
Ce que j'ai modifié ici est le suivant :
- L'intervalle du timer est maintenant de 10 ms, les threads ont encore 3000 ms. L'effet est que pendant que les threads dorment, de nouveaux threads sont créés.
- J'ai ajouté varialbe
t
qui compte le nombre de fils actuellement actifs (il est augmenté au début du fil et diminué juste avant la fin du fil)
- J'ai ajouté 2 instructions dump, imprimant le début et la fin du fil.
- Enfin, j'ai donné le paramètre de la fonction
doWork
un nom différent (h2), qui permet de voir la valeur de la variable sous-jacente h
Maintenant, il est intéressant de voir la sortie de ce programme modifié en LinqPad (notez que les valeurs ne sont pas toujours les mêmes car elles dépendent des conditions de course des threads lancés) :
h=1, h2=1, threads=1 [start]
h=2, h2=2, threads=2 [start]
h=3, h2=3, threads=3 [start]
h=4, h2=4, threads=4 [start]
h=5, h2=5, threads=5 [start]
...
h=190, h2=190, threads=190 [start]
h=191, h2=191, threads=191 [start]
h=192, h2=192, threads=192 [start]
h=193, h2=193, threads=193 [start]
h=194, h2=194, threads=194 [start]
h=194, h2=2, threads=192 [end]
h=194, h2=1, threads=192 [end]
h=194, h2=3, threads=191 [end]
h=195, h2=195, threads=192 [start]
Je pense que les valeurs parlent d'elles-mêmes : Ce qui se passe, c'est que toutes les 10 ms, un nouveau fil est lancé, alors que d'autres sont encore en sommeil. Il est également intéressant de voir que h n'est pas toujours égal à h2, surtout si plusieurs threads sont lancés pendant que d'autres dorment. Le nombre de threads (variable t) se stabilise après un certain temps, c'est-à-dire qu'il tourne autour de 190-194.
Vous pourriez argumenter que nous devons mettre des verrous sur les variables t et h, par exemple
readonly object o1 = new object();
int _t=0;
int t {
get {int tmp=0; lock(o1) { tmp=_t; } return tmp; }
set {lock(o1) { _t=value; }}
}
Bien que cette approche soit plus propre, elle n'a pas changé l'effet montré dans cet exemple.
Maintenant, afin de prouver que chaque thread dort réellement 3000ms (= 3s), ajoutons un fichier de type Stopwatch
au fil de travail doWork
:
public void doWork(int h2)
{
Stopwatch sw = new Stopwatch(); sw.Start();
try
{
t++; string.Format("h={0}, h2={1}, threads={2} [start]",
h, h2, t).Dump();
Thread.Sleep(3000); }
finally {
sw.Stop(); var tim = sw.Elapsed;
var elapsedMS = tim.Seconds*1000+tim.Milliseconds;
t--; string.Format("h={0}, h2={1}, threads={2} [end, sleep time={3} ms] ",
h, h2, t, elapsedMS).Dump();
}
}
Pour un nettoyage correct des threads, désactivons le timer après l'exécution de la fonction ReadLine
comme suit :
Console.ReadLine(); t.Enabled=false;
Cela vous permet de voir ce qui se passe si plus aucun fil ne démarre, après que vous ayez appuyé sur ENTER :
...
h=563, h2=559, threads=5 [end, sleep time=3105 ms]
h=563, h2=561, threads=4 [end, sleep time=3073 ms]
h=563, h2=558, threads=3 [end, sleep time=3117 ms]
h=563, h2=560, threads=2 [end, sleep time=3085 ms]
h=563, h2=562, threads=1 [end, sleep time=3054 ms]
h=563, h2=563, threads=0 [end, sleep time=3053 ms]
Vous pouvez voir qu'ils sont tous terminés l'un après l'autre comme prévu et qu'ils ont dormi environ 3s (ou 3000ms).