Est-ce que cela prend du temps de démarrer un Thread ?
Les pools
Fréquemment des ressources comme des connections à des bases de données, connections à un annuaire LDAP, références d'objets distants dont la recherche peut prendre du temps,... sont placées dans un pool.
C'est particulièrement vrai dans les environnements serveurs. Les clients y sont nombreux par définition et les services doivent être rendus en parallèle.
L'utilité est de permettre d'obtenir les ressources une seule fois ( ouverture de la connection à la base ), sans payer par la suite le temps pour obtenir à nouveau la ressource.
Une première solution pourrait être de n'ouvrir qu'une seule connection et la faire partager entre plusieurs threads. Cela pose des problèmes, notamment si deux clients font des modifications sur une base en mode transactionnel.
La solution couramment adoptée est d'utiliser un "pool". On place dans cet objet plusieurs connections qui peuvent être ouvertes à la création du pool ou au fur et à mesure des besoins.
Chaque client se voit confier par le pool une connection de manière exclusive. Chaque connection est replacée dans le pool une fois que le client a fini de l'utiliser. La responsabilité principale du pool est de gérer les attentes lorsque une demande de client ne peut être assouvie de suite. Si lorsque le client fait la requête aucune connection n'est disponible, le pool met en attente le thread du client jusquà ce qu'un autre client remette une connection dans le pool.
Cette attente est invisible pour l'utilisateur du pool. Il s'agit pour l'utilisateur du pool, d'obtenir une connection et de la rendre - un point c'est tout - sans plus de notion du multithreading.
Un ou plusieurs threads
Dans le cas où on veut obtenir des services asynchrones le problème est un peu différent.
Par exemple un serveur web reçoit une requête à l'issu de laquelle il doit générer du HTML, qui doit être envoyé au client HTTP le plus rapidement possible. Le serveur doit aussi envoyer un courrier, sans urgence. Il s'agit de rapidement répondre au client à travers une page HTML.
Un autre thread peut être démarré pour envoyer le courrier. Mais on dit parfois que démarrer un thread prend du temps. Est ce vrai ?
Si le démarrage d'un thread ne prend pas de temps, on peut se contenter de démarrer un thread par tache à exécuter. C'est beaucoup plus simple au niveau de la conception.
Par contre, si le démarrage d'un thread est trop long, on pourra se contenter de créer un seul thread auquel on enverra un message d'exécution de tache. La tache sera exécutée de manière asynchrone (plus tard) par rapport à la requête.
Méthode
Pour me rendre compte du temps pris par le démarrage d'un thread, j'ai écrit un petit banc de test. J'y exécute une tache simplifiée au maximum : afficher un numéro. La tache sera même mise en commentaire afin de mesurer uniquement le temps nécessaire au démarrage des threads et le temps de passage de la commande.
Les tests ont été menés sur un PIII 833 MHz. Vous vous rendrez compte que les temps en millisecondes ont été légérement arrondis. Ca change peu la réalité, au profit de la présentation.
Les résultats
Les chiffres sont éloquents. Le démarrage d'un thread prend à peu près 0,3 ms
L'exécution d'une requête asynchrone en mode monothread ne prend que 5 micro secondes
Exécution de 10000 taches
Exécution de 100000 taches
Exécution de 1000000 taches
Un seul thread
Un thread par tache
Un seul thread
Un thread par tache
Un seul thread
Un thread par tache
100
3000
550
30000
5000
300000
Conclusion
Pour des taches exécutées de manière asynchrone, fréquemment, il est intéressant pour les performances de créer peu de threads. Cela, malgré le coup de l'acquisition de la section critique nécessaire lors de l'utilisation de l'objet queue/pile commun à deux threads.
Néanmoins pour des taches asynchrones executées de manière peu fréquente, le surcoût de 0,3 ms sur un ordinateur moyen n'est pas si élevé que cela si le code peut être plus lisible.
Un bon concepteur objet saura tirer partie de l'encapsulation afin d'avoir un code lisible en surface et facile à utiliser.