Un thread est un chemin d'exécution dans un processus. Chaque processus a un ou plusieurs threads d'exécution. La fonction Win32 de création de processus CreateProcess, créée automatiquement un thread initial. Ce thread commence son exécution sur la fonction main (pour les applications console), sur la fonction WinMain (pour les applications GUI), ou sur la fonction ServiceMain (pour les services). Pour créer d'autres threads, un processus appelle al fonction Win32 CreateThread. Notez que le système exécute seulement des thread, jamais des processus. Les threads permettent à une application de faire plusieurs choses simultanément, ce qui permet d'apporter plus de fonctionnalités à une application ou une meilleure gestion de l'interface utilisateur. Sur les machines à plusieurs processeurs, plusieurs threads peuvent s'exécuter sur différents processeurs, améliorant ainsi les performances d'exécution.
Les appels normaux de fonctions sont synchrones, donc l'appelant ne reprend la main qu'une fois la fonction terminée:
// Some code here... MyFunc(); // Call MyFunc. // Some more code here... // Code here executes only when MyFunc has finished. |
La fonction CreateThread permet l'exécution des fonctions en asynchrone, donc l'appelant reprend immédiatement la main et n'a pas besoin d'attendre la fin d'exécution de la fonction:
// Some code here... CreateThread(..., MyFunc, ...); // Call MyFunc. // Some more code here... // Code here executes immediately; // MyFunc is executing concurrently. |
Quand un thread a fini de s'exécuter, vous pouvez appeler la fonction GetExitCodeThread pour obtenir le code de sortie du thread. Un thread (MyFunc dans le code précédent) se termine à la fin de son code ou après un appel à la fonction ExitThread. (Vous pouvez aussi utiliser la fonction TerminateThread pour créer explicitement un thread, mais ce n'est pas conseillé.)
Tous les objets du noyau, comme les threads, ont un compteur d'utilisation. Pour les threads, ce compteur est positionné initialement à 2. Lorsqu'un thread se termine, son compteur est décrémenté. Le créateur du thread doit aussi décrémenté ce compteur en appelant la fonction Win32 CloseHandle. Ainsi, le compteur d'utilisation de l'objet thread passe à 0 et les système détruit l'objet thread.
Des valeurs de priorité sont affectées à chaque processus et à chaque thread. Le scheduler de threads du noyau utilise ces valeurs dans son algorithme pour déterminer l'ordre dans lequel les threads sont ordonnancés pour y être exécuté. Vous pouvez ajuster la valeur de priorité d'un processus via la fonction SetPriorityClass, et SetThreadPriority pour contrôler la priorité d'un thread. Les deux valeurs sont combinées pour produire la priorité de base du thread. Vous pouvez appeler les fonctions GetPriorityClass et GetThreadPriority pour obtenir ces paramètres.
Un des aspects essentiels de l'utilisation des threads est la synchronisation. Les accès non protégés à des données partagées par plusieurs threads peuvent produire des violations d'accès (access violation). Par exemple, si deux threads manipulent une structure de données non protégée, un thread qui commence de lire des données dans la structure est interrompu par le second thread qui réalise une opération d'écriture. Lorsque le premier thread reprend son opération de lecture, les données ne sont plus dans un état consistant et cela provoque une exception système.
Ce type de problème de synchronisation est difficile à corriger car le phénomène se produit aléatoirement et est difficile à reproduire. Win32 offre plusieurs objets pour la synchronisation des threads: les événements (events), les sections critique (critical sections), les sémaphores (semaphores), et les mutex. Vous devez utiliser les objets Win32 de synchronisation de thread pour garantir qu'un seul thread à un moment donné utilise les données partagées par l'ensemble des threads d'une application. Les variables automatiques stockées sur la pile d'un thread ne posent pas de difficultés car elles sont privées au thread; il en va de même pour le stockage de thread local (TLS Thread Local Storage). Windows supporte aussi les fibres (fibers), des threads allégés, qui peuvent être ordonnancés manuellement.