Multithreading
Introduction
La programmation multithread implique la répartition des tâches (thread) d’une même application de manière à ce qu’elles soient réalisées indépendamment les unes des autres afin d’exploiter au mieux le temps du processeur. Les tâches ne se réalisent pas réellement en parallèle. Le processeur affecte un temps de traitement à chaque tâche en fonction de son importance et fait lui-même le basculement de l’une à l’autre. Ce basculement de contexte implique que le processeur mémorise la pile de la tâche en cours avant de restaurer celle de la tâche à laquelle il redonne la main. C# 5 a introduit de nouveaux mots-clés (await et async) afin de faciliter le développement asynchrone. Le développement synchrone implique qu’une fonction appelée bloque l’exécution du programme jusqu’à ce qu’elle soit terminée. Lorsqu’une fonction est appelée de manière asynchrone, l’exécution du programme principal continue. Il y a donc une notion d’exécution en parallèle et de concurrence comme pour la programmation multithread.
Les exemples de ce chapitre sont disponibles dans les sources sous le projet MultiThreading de la solution.
La classe Thread
L’espace de noms System.Threading héberge les classes permettant de créer et contrôler les tâches, notamment avec la classe Thread. Les threads doivent être utilisés lorsqu’une application doit gérer plusieurs tâches indépendantes comme gérer une interface utilisateur et réaliser un traitement de données. L’application aura de meilleures performances si ces deux tâches se déroulent dans des threads spécifiques.
1. Créer un thread
Le délégué ThreadStart est employé pour créer un nouveau thread. Son constructeur prend comme argument la signature de la méthode qui sera exécutée dans ce nouveau thread :
ThreadStart newThread = new ThreadStart(OtherThread);
Le délégué est ensuite passé en paramètre du constructeur de l’objet Thread :
Thread thread = new Thread(newThread);
Ensuite, on appelle la méthode Start de l’objet Thread pour lancer l’appel à la méthode du délégué ThreadStart :
thread.Start();
L’exemple complet sous forme d’application console est le suivant :
static void Main(string[] args)
{
Console.WriteLine("Lancement du thread principal");
ThreadStart newThread = new ThreadStart(OtherThread);
Thread thread = new Thread(newThread);
thread.Start();
Console.WriteLine("Thread principal terminé");
}
public static void OtherThread()
{
Console.WriteLine("Lancement du thread secondaire");
Console.WriteLine("Thread secondaire terminé");
}
Lorsque vous lancez cette application ([F5]), la console affiche le résultat suivant :
Lancement du thread principal
Thread principal terminé
Lancement du thread secondaire
Thread secondaire terminé
Vous pouvez remarquer que la méthode Main s’est complètement exécutée avant même que la méthode OtherThread n’ait débuté. Cela prouve que les deux méthodes se sont exécutées...
Fonctions asynchrones
La version 5 du langage C# a introduit les nouveaux mots-clés async et await ayant pour but de faciliter l’implémentation de fonctions asynchrones de manière à ce qu’elle soit semblable à l’implémentation d’une méthode synchrone.
Voici l’exemple d’une fonction qui demande un certain temps à être réalisée :
public static double TimeConsumingFunction()
{
double x = 1;
for (int i = 1; i < 100000000; i++)
{
x += Math.Tan(x) / i;
}
return x;
}
Cette fonction bloque l’exécution de la suite du programme tant qu’elle n’a pas terminé et retourné son résultat à l’appelant :
Console.WriteLine("Lancement d'une fonction longue");
DateTime debut = DateTime.Now;
double resultat = TimeConsumingFunction();
DateTime fin = DateTime.Now;
Console.Write("Résultat : ");
Console.WriteLine(resultat);
Console.Write("Temps d'exécution : ");
Console.WriteLine(TimeSpan.FromTicks(fin.Ticks -
debut.Ticks).TotalSeconds);
1. Task et Task<TResult>
L’espace de noms System.Threading.Tasks définit une classe Task et une classe Task<TResult> qui représentent une méthode qui se complète dans le futur. Vous pouvez obtenir un objet...
Le composant BackgroundWorker
Le composant BackgroundWorker est utilisé dans le formulaire Send de la solution de démonstration.
La boîte à outils de Visual Studio fournit le composant BackgroundWorker. Il est très utile pour les applications Windows qui doivent à la fois gérer une interface utilisateur et son rafraîchissement mais aussi effectuer des opérations lourdes.
Comme indiqué par l’interface, le composant BackgroundWorker permet d’exécuter une opération dans un thread séparé. Il suffit d’instancier un nouvel objet BackgroundWorker soit via le concepteur de vues, soit dans le code et de lui indiquer la méthode à exécuter dans un thread séparé :
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
Lorsque vous souhaitez lancer l’exécution des tâches en arrière-plan, appelez la méthode RunWorkerAsync de l’objet BackgroundWorker :
bw.RunWorkerAsync();
Le composant a été simplifié au maximum pour faciliter son utilisation. Il contient deux propriétés WorkerReportsProgress et WorkerSupportsCancellation permettant respectivement de spécifier si le composant signale sa progression aux objets abonnés à son événement ProgressChanged et si l’exécution...