Cours sur le thread pool en win32

Développement système natif en c/c++ avec win32 ...

Moderator: Rick

Post Reply
Hydraxx
Site Admin
Posts: 46
Joined: Mon Jan 12, 2026 4:04 pm
Location: France
Contact:

Cours sur le thread pool en win32

Post by Hydraxx »

Salut, :D

aujourd’hui on va voir un composant vraiment fondamental de Windows moderne : le Thread Pool Win32.

C’est un sujet important, parce qu’en pratique énormément de code système repose dessus, parfois sans que le développeur s’en rende meme compte. Services Windows, I/O asynchrones, timers, attente sur handles, exécution différée, callbacks réseau, composants COM, travail interne de bibliothèques système… le Thread Pool est partout.

Le problème, c’est que beaucoup de développeurs restent bloqués sur un modèle ancien : un travail = un thread = un CreateThread. Ce modèle peut marcher sur de petits exemples, mais il devient vite mauvais dès qu’on parle de charge réelle, de latence, de consommation mémoire, de context switch, de blocage, ou simplement d’architecture propre.

Le Thread Pool Win32 existe justement pour éviter ce genre d’erreurs.

Pourquoi le Thread Pool est un sujet fondamental

Comprendre le Thread Pool Win32 permet de mieux voir :
  • comment Windows exécute du travail concurrent sans créer un thread pour chaque tâche
  • pourquoi CreateThread n’est pas toujours une bonne solution
  • comment soumettre du travail ponctuel, différé ou lié à des I/O
  • comment intégrer timers, waits et callbacks dans un modèle cohérent
  • comment Windows réutilise ses threads plutôt que de les recréer sans cesse
  • comment structurer un code plus scalable et plus propre
Autrement dit, comprendre le Thread Pool, c’est commencer à penser comme Windows moderne : on ne raisonne plus d’abord en “je veux tel thread”, mais en “je veux que telle tâche soit effectuée dans de bonnes conditions”.

Ce qu’est réellement le Thread Pool Win32

Le Thread Pool Win32 est un mécanisme fourni par Windows pour gérer automatiquement un ensemble de threads de travail réutilisables. Plutot que de créer un thread à chaque besoin, le système maintient un pool de threads, ajuste leur usage, planifie les callbacks, et réemploie les threads existants quand cela a du sens.

Le développeur ne manipule donc plus principalement des threads, mais des unités de travail et des objets associés.

Windows se charge notamment de :
  • créer les threads nécessaires
  • les réutiliser
  • limiter les créations inutiles
  • équilibrer la charge selon le contexte
  • éviter une partie de la surcharge du modèle “un thread partout”
Cela ne veut pas dire que toute décision magique est parfaite en toute circonstance, mais cela veut dire que le modèle par défaut est bien meilleur que de multiplier manuellement les CreateThread pour chaque événement.

Pourquoi CreateThread devient vite une mauvaise approche

CreateThread n’est pas “interdit”. C’est une API légitime, utile dans certains cas précis. Mais elle ne doit pas devenir un réflexe universel.

Créer un thread manuellement implique plusieurs couts :
  • coût de création et d’initialisation du thread
  • stack dédiée pour chaque thread
  • coût de scheduling supplémentaire
  • augmentation des context switch
  • pression sur les ressources du processus et du système
  • difficulté croissante à superviser et synchroniser tout cela
Dans un petit programme, on ne le sent pas forcément. Sous charge réelle, cela devient vite plus visible.

Le problème n’est donc pas que CreateThread soit “mauvaise” en soi, mais qu’elle est trop bas niveau et trop coûteuse pour servir de réponse systématique à tout besoin concurrent.

Le vrai changement de modèle mental

Le Thread Pool impose un changement de logique très important.

Ancien modèle simpliste :
  • j’ai une tâche
  • je crée un thread
  • le thread exécute la tâche
  • je détruis le thread
Modèle Thread Pool :
  • j’ai une tâche
  • je décris un callback
  • je soumets cette tâche au système
  • Windows choisit quand et sur quel thread du pool l’exécuter
Le développeur perd le contrôle précis du thread utilisé, mais gagne un modèle global bien plus sain, plus scalable et généralement plus efficace.

Les grands objets du Thread Pool Win32

Le Thread Pool Win32 repose sur plusieurs familles d’objets. Les plus importantes à connaitre sont :
  • Work
  • Timer
  • Wait
  • I/O
  • Cleanup Group
  • Thread Pool personnalisé
  • Environment de callback
Les quatre objets les plus vus au début sont bien sûr :
  • Work : tâche ponctuelle
  • Timer : callback différé ou périodique
  • Wait : callback déclenché quand un handle devient signalé
  • I/O : callback lié à une I/O asynchrone
Mais il faut aussi connaitre les objets de configuration avancée, sinon on ne voit qu’une partie du modèle réel.

Le callback : cœur du modèle

Tout passe par des callbacks.

Un callback du Thread Pool est du code exécuté par Windows sur un thread appartenant au pool. Cela implique immédiatement plusieurs conséquences :
  • le callback doit être court si possible
  • le callback ne doit pas bloquer longtemps sans nécessité
  • le callback doit éviter les opérations qui paralysent inutilement un thread du pool
  • le callback doit gérer proprement sa synchronisation si des données sont partagées
Il faut bien comprendre qu’un thread du pool est une ressource partagée par le système. Le monopoliser inutilement, c’est dégrader potentiellement l’efficacité globale.

Types fondamentaux du Thread Pool

Pour lire ou écrire du code Thread Pool, il faut reconnaitre plusieurs types importants :
  • PTP_WORK
  • PTP_TIMER
  • PTP_WAIT
  • PTP_IO
  • PTP_POOL
  • PTP_CLEANUP_GROUP
  • PTP_CALLBACK_ENVIRON
  • PTP_CALLBACK_INSTANCE
  • TP_WAIT_RESULT
Ces types apparaissent dans presque toutes les signatures du domaine.

PTP_CALLBACK_INSTANCE : à quoi ça sert

Un paramètre souvent ignoré par les débutants dans les callbacks est :
  • PTP_CALLBACK_INSTANCE
Il représente le contexte d’exécution du callback. On ne s’en sert pas dans tous les exemples simples, mais il devient utile pour certaines fonctions avancées du Thread Pool, par exemple :
  • CallbackMayRunLong
  • DisassociateCurrentThreadFromCallback
  • LeaveCriticalSectionWhenCallbackReturns
  • ReleaseMutexWhenCallbackReturns
  • SetEventWhenCallbackReturns
  • FreeLibraryWhenCallbackReturns
Autrement dit, ce n’est pas un paramètre “décoratif”. Il fait partie des mécanismes avancés de gestion du cycle de vie du callback.

Le Work : l’objet de base

Le type le plus simple et souvent le plus utilisé pour commencer est le Work.

Il sert à exécuter une tâche ponctuelle sur un thread du pool. On l’utilise volontiers pour :
  • du calcul
  • du traitement mémoire
  • de la compression
  • du chiffrement
  • du parsing
  • du post-traitement
  • une tâche indépendante sans nécessité de thread dédié
Callback type :

Code: Select all

VOID CALLBACK WorkCallback(
    PTP_CALLBACK_INSTANCE Instance,
    PVOID Context,
    PTP_WORK Work
)
{
    int value = *(int*)Context;
    printf("Work exécuté : %d\n", value);
}
Création :

Code: Select all

PTP_WORK work = CreateThreadpoolWork(
    WorkCallback,
    &data,
    NULL
);

if (!work)
    return 1;
Soumission :

Code: Select all

SubmitThreadpoolWork(work);
Il faut ensuite attendre correctement si nécessaire, puis fermer :
  • WaitForThreadpoolWorkCallbacks
  • CloseThreadpoolWork
Exemple plus propre :

Code: Select all

int main()
{
    int data = 42;

    PTP_WORK work = CreateThreadpoolWork(
        WorkCallback,
        &data,
        NULL
    );

    if (!work)
        return 1;

    SubmitThreadpoolWork(work);

    WaitForThreadpoolWorkCallbacks(work, FALSE);
    CloseThreadpoolWork(work);

    return 0;
}
Il vaut mieux éviter le vieux réflexe “Sleep(1000) pour laisser le callback finir”. Ce n’est pas une vraie synchronisation.

WaitForThreadpoolWorkCallbacks

Cette API est très importante :
  • WaitForThreadpoolWorkCallbacks
Elle permet d’attendre la fin des callbacks associés à un objet work. Son second paramètre permet aussi d’exprimer si l’on veut annuler les callbacks encore en attente avant exécution.

C’est bien plus propre que des Sleep arbitraires.

Le Timer du Thread Pool

Le Timer permet d’exécuter un callback après un délai, ou de manière périodique.

Il est très utile pour :
  • des tâches planifiées
  • des timeouts
  • de la supervision périodique
  • des retries
  • des watchdogs simples
Signature du callback timer :

Code: Select all

VOID CALLBACK TimerCallback(
    PTP_CALLBACK_INSTANCE Instance,
    PVOID Context,
    PTP_TIMER Timer
)
{
    printf("Timer déclenché\n");
}
Création :

Code: Select all

PTP_TIMER timer = CreateThreadpoolTimer(
    TimerCallback,
    NULL,
    NULL
);

if (!timer)
    return 1;
Armement :

Code: Select all

FILETIME ft;
ULONGLONG delay = (ULONGLONG)-50000000;

ft.dwLowDateTime  = (DWORD)delay;
ft.dwHighDateTime = (DWORD)(delay >> 32);

SetThreadpoolTimer(timer, &ft, 0, 0);
Le délai est exprimé en temps absolu ou relatif selon la valeur passée. Une valeur négative encode un délai relatif en unités de 100 nanosecondes.

Il faut aussi connaitre :
  • WaitForThreadpoolTimerCallbacks
  • CloseThreadpoolTimer
  • SetThreadpoolTimerEx
Le timer peut être ponctuel ou périodique selon les paramètres.

SetThreadpoolTimer et temps relatifs

Le point qui perturbe souvent au début, c’est le format du temps. Le Thread Pool timer n’utilise pas directement un simple nombre de millisecondes pour la première date de déclenchement. On passe un FILETIME représentant une échéance absolue, ou une échéance relative si la valeur est négative.

Cela explique l’usage fréquent de ULONGLONG négatif casté dans un FILETIME.

Il faut donc comprendre au moins :
  • temps relatif = valeur négative
  • unité = 100 nanosecondes
  • le paramètre de période, lui, est en millisecondes
Le Wait du Thread Pool

Le Wait permet de déclencher un callback lorsqu’un handle devient signalé. C’est extrêmement pratique pour intégrer la synchronisation Win32 dans le modèle du Thread Pool.

On l’utilise par exemple avec :
  • des events
  • des processus
  • des sémaphores
  • des timers waitables
  • d’autres objets noyau signalables
Signature :

Code: Select all

VOID CALLBACK WaitCallback(
    PTP_CALLBACK_INSTANCE Instance,
    PVOID Context,
    PTP_WAIT Wait,
    TP_WAIT_RESULT Result
)
{
    printf("Objet signalé\n");
}
Création :

Code: Select all

PTP_WAIT wait = CreateThreadpoolWait(
    WaitCallback,
    NULL,
    NULL
);

if (!wait)
    return 1;
Armement :

Code: Select all

SetThreadpoolWait(wait, hEvent, NULL);
Puis lorsque l’objet devient signalé, Windows exécute le callback sur un thread du pool.

Il faut également connaitre :
  • WaitForThreadpoolWaitCallbacks
  • CloseThreadpoolWait
Exemple plus propre :

Code: Select all

int main()
{
    HANDLE hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
    if (!hEvent)
        return 1;

    PTP_WAIT wait = CreateThreadpoolWait(
        WaitCallback,
        NULL,
        NULL
    );

    if (!wait)
    {
        CloseHandle(hEvent);
        return 1;
    }

    SetThreadpoolWait(wait, hEvent, NULL);

    SetEvent(hEvent);

    WaitForThreadpoolWaitCallbacks(wait, FALSE);
    CloseThreadpoolWait(wait);
    CloseHandle(hEvent);

    return 0;
}
TP_WAIT_RESULT

Le paramètre TP_WAIT_RESULT du callback wait indique le résultat de l’attente. Il est important notamment pour distinguer certains cas comme un timeout.

Cela devient utile dès qu’on utilise un délai dans SetThreadpoolWait.

Le Thread Pool I/O

L’intégration des I/O asynchrones avec le Thread Pool est un des aspects les plus puissants du modèle.

Ici, on ne parle plus d’un simple callback arbitraire, mais d’un callback déclenché quand une opération I/O overlapped associée à un objet éligible se termine.

Le type central est :
  • PTP_IO
Signature du callback I/O :

Code: Select all

VOID CALLBACK IoCallback(
    PTP_CALLBACK_INSTANCE Instance,
    PVOID Context,
    PVOID Overlapped,
    ULONG IoResult,
    ULONG_PTR BytesTransferred,
    PTP_IO Io
)
{
    printf("I/O terminé : %llu bytes\n", (unsigned long long)BytesTransferred);
}
Création :

Code: Select all

HANDLE file = CreateFileW(
    L"test.txt",
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED,
    NULL
);

if (file == INVALID_HANDLE_VALUE)
    return 1;

PTP_IO io = CreateThreadpoolIo(
    file,
    IoCallback,
    NULL,
    NULL
);

if (!io)
{
    CloseHandle(file);
    return 1;
}
Pour l’utiliser correctement, il faut comprendre plusieurs API importantes :
  • CreateThreadpoolIo
  • StartThreadpoolIo
  • CancelThreadpoolIo
  • WaitForThreadpoolIoCallbacks
  • CloseThreadpoolIo
Le flux correct consiste généralement à appeler StartThreadpoolIo juste avant l’I/O overlapped, puis à lancer réellement l’opération.

Si l’I/O échoue immédiatement sans partir réellement, il faut souvent appeler CancelThreadpoolIo pour garder un état cohérent.

Thread Pool I/O et OVERLAPPED

Le Thread Pool I/O repose sur le modèle OVERLAPPED déjà connu des I/O asynchrones Win32.

Il faut donc connaitre ou reconnaitre :
  • OVERLAPPED
  • ReadFile / WriteFile avec FILE_FLAG_OVERLAPPED
  • GetOverlappedResult
Le Thread Pool I/O ne remplace pas le modèle OVERLAPPED, il s’y intègre.

Autrement dit :
  • l’objet I/O doit être ouvert en mode overlapped si nécessaire
  • l’opération doit etre une vraie I/O asynchrone compatible
  • le callback du pool sera appelé à la terminaison
Ce que veut dire “un callback doit être rapide”

Quand on dit qu’un callback de Thread Pool doit être rapide, cela ne veut pas dire “il doit obligatoirement finir en 2 microsecondes”. Cela veut dire qu’il faut éviter de monopoliser inutilement un thread du pool avec des attentes longues ou une logique qui n’a rien à faire là.

A éviter dans un callback si possible :
  • Sleep de longue durée
  • WaitForSingleObject infini sur une ressource externe
  • boucles bloquantes longues
  • I/O synchrones lentes sans raison
  • verrous pris trop longtemps
Si un callback risque d’etre long, il existe des mécanismes pour le signaler ou il faut repenser l’architecture.

API importante à connaitre ici :
  • CallbackMayRunLong
Elle permet de signaler au système que le callback courant risque de durer, ce qui aide le Thread Pool à mieux s’ajuster.

Synchronisation dans les callbacks

Le fait que Windows gère les threads ne supprime pas les problèmes de synchronisation. Si plusieurs callbacks touchent les mêmes données, il faut toujours protéger ce qui doit l’être.

API et mécanismes typiques :
  • InterlockedIncrement
  • InterlockedDecrement
  • InterlockedExchange
  • InterlockedCompareExchange
  • CRITICAL_SECTION
  • SRWLOCK
  • events
  • sémaphores
Le Thread Pool simplifie la gestion des threads, pas la réalité de la concurrence.

Cleanup Group : très important pour gérer proprement un ensemble

Un des aspects les plus intéressants du Thread Pool Win32 est la possibilité de regrouper plusieurs objets dans une cleanup group.

Types et API importants :
  • PTP_CLEANUP_GROUP
  • CreateThreadpoolCleanupGroup
  • CloseThreadpoolCleanupGroup
  • CloseThreadpoolCleanupGroupMembers
  • SetThreadpoolCallbackCleanupGroup
L’idée est simple : on crée un groupe de nettoyage, on associe plusieurs objets Thread Pool à ce groupe via un environnement de callback, puis on peut fermer ou annuler l’ensemble de manière cohérente.

C’est extrêmement utile dans une architecture sérieuse.

Callback environment : personnaliser le comportement

Le Thread Pool possède aussi une notion de callback environment.

Type important :
  • TP_CALLBACK_ENVIRON
API/macro importantes :
  • InitializeThreadpoolEnvironment
  • DestroyThreadpoolEnvironment
  • SetThreadpoolCallbackPool
  • SetThreadpoolCallbackCleanupGroup
  • SetThreadpoolCallbackRunsLong
  • SetThreadpoolCallbackPriority
  • SetThreadpoolCallbackLibrary
Avec cet environnement, on peut par exemple :
  • choisir un pool personnalisé
  • lier les objets à une cleanup group
  • indiquer qu’un callback peut être long
  • influencer certains aspects de priorité ou de cycle de vie
C’est une partie qu’il faut connaitre, parce qu’elle fait passer du “petit exemple de démo” à une vraie architecture configurable.

Créer son propre pool de threads

Il n’existe pas qu’un pool global implicite. On peut aussi créer un thread pool personnalisé.

API importantes :
  • CreateThreadpool
  • CloseThreadpool
  • SetThreadpoolThreadMaximum
  • SetThreadpoolThreadMinimum
Exemple schématique :

Code: Select all

PTP_POOL pool = CreateThreadpool(NULL);
if (!pool)
    return 1;

SetThreadpoolThreadMaximum(pool, 4);

if (!SetThreadpoolThreadMinimum(pool, 1))
{
    CloseThreadpool(pool);
    return 1;
}
Cela permet d’avoir un meilleur contrôle dans certains scénarios, par exemple pour isoler une charge spécifique ou imposer certaines limites de concurrence.

Pool global contre pool personnalisé

Le pool par défaut fourni par Windows suffit dans beaucoup de cas. Mais un pool personnalisé devient utile lorsqu’on veut :
  • isoler une sous-partie de l’application
  • limiter la concurrence d’un groupe précis de tâches
  • éviter qu’un domaine de travail perturbe un autre
  • associer explicitement des objets à un environnement contrôlé
C’est particulièrement pertinent dans du code système, des services, ou des composants riches.

Priorité et bibliothèque dans l’environnement de callback

Il faut aussi connaitre de nom plusieurs réglages plus avancés :
  • SetThreadpoolCallbackPriority
  • SetThreadpoolCallbackLibrary
Le premier permet d’influencer la priorité de callback dans certaines limites du modèle. Le second est utile pour lier proprement un callback à une bibliothèque, notamment pour éviter certains problèmes de durée de vie lors du déchargement de DLL.

Callbacks et durée de vie des objets

Un problème classique du Thread Pool concerne la durée de vie du contexte passé aux callbacks.

Par exemple, si un callback reçoit un pointeur Context vers une structure locale ou détruite trop tôt, le programme peut crasher ou corrompre sa mémoire.

Il faut donc avoir une discipline stricte :
  • le contexte passé au callback doit rester valide tant que le callback peut encore l’utiliser
  • la fermeture de l’objet Thread Pool ne doit pas être faite naïvement sans attendre ou annuler correctement
  • les cleanup groups et les fonctions WaitForThreadpool* aident beaucoup ici
Fonctions “WhenCallbackReturns”

Le Thread Pool propose aussi des fonctions très pratiques pour exécuter automatiquement certaines actions quand le callback se termine.

API importantes :
  • FreeLibraryWhenCallbackReturns
  • LeaveCriticalSectionWhenCallbackReturns
  • ReleaseMutexWhenCallbackReturns
  • ReleaseSemaphoreWhenCallbackReturns
  • SetEventWhenCallbackReturns
Elles utilisent le PTP_CALLBACK_INSTANCE courant et permettent d’écrire du code plus sûr autour du cleanup du callback.

DisassociateCurrentThreadFromCallback

Une autre API avancée importante est :
  • DisassociateCurrentThreadFromCallback
Elle sert dans certains scénarios où l’on veut signaler que le thread courant ne doit plus être considéré comme logiquement attaché au callback pour certains mécanismes de synchronisation du Thread Pool. Ce n’est pas une API de débutant, mais il faut au moins connaitre son existence, car elle apparait dans des architectures plus avancées.

Annulation et arrêt propre

Le Thread Pool n’est pas seulement une API de création. Il faut aussi savoir fermer et attendre proprement.

Pour chaque type d’objet, il existe en général une paire logique :
  • attendre les callbacks
  • fermer l’objet
API importantes à retenir :
  • WaitForThreadpoolWorkCallbacks
  • WaitForThreadpoolTimerCallbacks
  • WaitForThreadpoolWaitCallbacks
  • WaitForThreadpoolIoCallbacks
  • CloseThreadpoolWork
  • CloseThreadpoolTimer
  • CloseThreadpoolWait
  • CloseThreadpoolIo
Dans un vrai code, c’est une erreur classique de fermer brutalement sans raisonner sur les callbacks encore en attente ou en cours.

Différence avec les vieux QueueUserWorkItem / BindIoCompletionCallback

Il faut aussi savoir que Windows possédait ou possède d’autres mécanismes voisins, comme :
  • QueueUserWorkItem
  • BindIoCompletionCallback
Ils font partie de l’histoire du Thread Pool Windows, mais l’API moderne structurée du Thread Pool est plus riche et plus cohérente. Il est donc bon de connaitre ces noms, mais aussi de comprendre qu’ils ne représentent pas le modèle le plus complet aujourd’hui.

Quand le Thread Pool est un bon choix

Le Thread Pool est particulièrement adapté quand :
  • on a des tâches ponctuelles ou fréquentes sans besoin de thread dédié
  • on veut intégrer timers, waits et I/O dans un cadre homogène
  • on veut éviter la prolifération de threads manuels
  • on écrit un service ou un composant longue durée
  • on vise une architecture plus scalable
En revanche, il faut parfois réfléchir davantage si l’on a un besoin très spécifique de thread dédié avec affinité, durée de vie contrôlée et logique très particulière.

Structures, types et API à connaitre absolument

Pour avoir déjà une base propre, il faut reconnaitre rapidement :
  • PTP_WORK
  • PTP_TIMER
  • PTP_WAIT
  • PTP_IO
  • PTP_POOL
  • PTP_CLEANUP_GROUP
  • PTP_CALLBACK_ENVIRON
  • PTP_CALLBACK_INSTANCE
  • TP_WAIT_RESULT
  • FILETIME
  • OVERLAPPED
Les API vraiment importantes sont :
  • CreateThreadpoolWork
  • SubmitThreadpoolWork
  • WaitForThreadpoolWorkCallbacks
  • CloseThreadpoolWork
  • CreateThreadpoolTimer
  • SetThreadpoolTimer
  • SetThreadpoolTimerEx
  • WaitForThreadpoolTimerCallbacks
  • CloseThreadpoolTimer
  • CreateThreadpoolWait
  • SetThreadpoolWait
  • WaitForThreadpoolWaitCallbacks
  • CloseThreadpoolWait
  • CreateThreadpoolIo
  • StartThreadpoolIo
  • CancelThreadpoolIo
  • WaitForThreadpoolIoCallbacks
  • CloseThreadpoolIo
  • CreateThreadpool
  • CloseThreadpool
  • SetThreadpoolThreadMaximum
  • SetThreadpoolThreadMinimum
  • CreateThreadpoolCleanupGroup
  • CloseThreadpoolCleanupGroup
  • CloseThreadpoolCleanupGroupMembers
  • InitializeThreadpoolEnvironment
  • DestroyThreadpoolEnvironment
  • SetThreadpoolCallbackPool
  • SetThreadpoolCallbackCleanupGroup
  • SetThreadpoolCallbackRunsLong
  • SetThreadpoolCallbackPriority
  • SetThreadpoolCallbackLibrary
  • CallbackMayRunLong
  • DisassociateCurrentThreadFromCallback
  • SetEventWhenCallbackReturns
  • LeaveCriticalSectionWhenCallbackReturns
  • ReleaseMutexWhenCallbackReturns
  • ReleaseSemaphoreWhenCallbackReturns
  • FreeLibraryWhenCallbackReturns
Erreurs classiques

Comme souvent en développement système, les erreurs les plus gênantes viennent d’un mauvais modèle mental.

Parmi les fautes fréquentes :
  • utiliser Sleep au lieu des fonctions WaitForThreadpool* pour “laisser le callback finir”
  • passer un contexte dont la durée de vie est trop courte
  • bloquer longtemps un callback sans réfléchir
  • oublier de fermer les objets Thread Pool
  • ne pas attendre correctement avant destruction
  • croire que le Thread Pool supprime tous les problèmes de synchronisation
  • oublier StartThreadpoolIo avant une I/O overlapped
  • ne pas annuler proprement quand une I/O ne part pas
  • ignorer les cleanup groups dans une architecture un peu sérieuse
Ce qu’il faut retenir

Le Thread Pool Win32 est le modèle moderne de gestion du travail concurrent coté Windows. Il permet de confier des unités de travail au système plutôt que de créer un thread pour chaque besoin. Windows gère alors la réutilisation, l’exécution et une partie de la planification de manière bien plus efficace que le modèle naïf fondé sur CreateThread partout.

Les idées essentielles à garder sont les suivantes :
  • on ne raisonne plus seulement en threads, mais en tâches et callbacks
  • Work, Timer, Wait et I/O sont les quatre objets les plus importants au début
  • les callbacks doivent rester aussi courts et propres que possible
  • les waits corrects et la fermeture propre sont indispensables
  • les cleanup groups et callback environments sont très importants dans une vraie architecture
  • le Thread Pool s’intègre très bien avec les I/O overlapped et la synchronisation Win32
  • c’est l’un des grands modèles recommandés pour le code concurrent moderne en Win32
Conclusion

Comprendre le Thread Pool Win32, c’est comprendre comment Windows moderne exécute une grande partie du travail parallèle sans tomber dans le piège “un thread pour chaque chose”. C’est aussi comprendre qu’il existe un vrai cadre unifié pour les tâches, les timers, les waits, les I/O et le nettoyage des callbacks.

Si tu maitrises déjà correctement :
  • CreateThreadpoolWork et SubmitThreadpoolWork
  • CreateThreadpoolTimer et SetThreadpoolTimer
  • CreateThreadpoolWait et SetThreadpoolWait
  • CreateThreadpoolIo, StartThreadpoolIo et WaitForThreadpoolIoCallbacks
  • WaitForThreadpoolWorkCallbacks / TimerCallbacks / WaitCallbacks / IoCallbacks
  • PTP_CALLBACK_ENVIRON, cleanup groups et pool personnalisé
  • CallbackMayRunLong et les fonctions WhenCallbackReturns
alors tu possèdes déjà une très bonne base sur le Thread Pool Win32.

Et surtout, tu commences à voir que le vrai sujet n’est pas seulement “comment lancer du code sur un autre thread”, mais “comment laisser Windows exécuter intelligemment le travail concurrent dans un cadre propre et scalable”.

A bientot pour le prochain cours :D

Who is online

Users browsing this forum: No registered users and 0 guests