Pourquoi .net Threadpool est-il utilisé uniquement pour des tâches de courte durée?

J’ai lu à de nombreux endroits que .net Threadpool est destiné à des tâches de courte durée (peut ne pas dépasser 3 secondes). Dans toutes ces citations, je n’ai pas trouvé de raison concrète de ne pas l’utiliser.

Même certaines personnes ont dit que cela entraînait des résultats désagréables si nous utilisions des tâches de longue durée et conduisait également à des impasses.

Quelqu’un peut-il expliquer cela en anglais clair avec une raison technique de ne pas utiliser de pool de threads pour des tâches de longue durée?

Pour être précis, je voudrais même donner un scénario et vouloir savoir pourquoi ThreadPool ne devrait pas être utilisé dans ce scénario avec des raisons appropriées.

Scénario: Je dois traiter des milliers de données d’utilisateurs. Les données de traitement de l’utilisateur sont extraites d’une firebase database locale. À l’aide de ces informations, je dois me connecter à une API hébergée ailleurs et la réponse de l’API sera stockée dans la firebase database locale après traitement.

Si quelqu’un peut m’expliquer les pièges de ce scénario si j’utilise ThreadPool avec une limite de threads de 20? Le temps de traitement de chaque utilisateur peut aller de 3 secondes à 1 minute (ou plus).

L’objective du pool de threads est d’éviter la situation dans laquelle le temps passé à créer le thread est plus long que le temps passé à l’ utiliser . En réutilisant les threads existants, nous en arrivons à éviter cette surcharge.

L’inconvénient est que le pool de threads est une ressource partagée: si vous utilisez un thread, quelque chose d’autre ne le peut pas. Ainsi, si vous avez beaucoup de tâches de longue durée, vous risquez de vous retrouver avec une famine de thread-pools, pouvant même conduire à une impasse.

N’oubliez pas que le code de votre application peut ne pas être le seul code utilisant le pool de threads … le code système l’utilise également beaucoup.

Il semble que vous souhaitiez peut -être avoir votre propre queue producteur / consommateur, avec un petit nombre de threads le traitant. Si vous pouviez également communiquer avec votre autre service à l’aide d’une API asynchrone, vous constaterez peut-être que chaque traitement sur votre ordinateur est de courte durée.

Cela est lié au fonctionnement du planificateur de pool de threads. Il s’efforce de ne pas libérer plus de threads en attente que de cœurs de processeur. Ce qui est une bonne idée, faire fonctionner plus de threads que de cœurs est une perte de temps puisque Windows passe du temps à changer de contexte entre les threads. Rendre le temps total nécessaire pour terminer les travaux plus longtemps.

Dès qu’un thread TP est terminé, un autre est autorisé à s’exécuter. Deux fois par seconde, le planificateur de transactions TP intervient lorsque les threads en cours d’exécution ne se terminent pas. Il ne peut pas dire pourquoi ces threads prennent autant de temps pour faire leur travail. Une demi-seconde, c’est beaucoup de cycles du processeur, un milliard de cool. Il suppose donc que les threads sont en train de se bloquer, en attendant qu’une sorte d’E / S se termine. Comme une requête dbase, une lecture de disque, une tentative de connexion de socket, des choses comme ça.

Et cela permet à un autre thread de s’exécuter. Vous avez maintenant plus de threads que de cœurs. Ce qui ne pose pas vraiment de problème si ces threads d’origine bloquent, ils ne consumnt aucun cycle du processeur.

Vous pouvez voir où cela mène: si votre thread fonctionne pendant 3 secondes, il crée un peu une impasse. Cela retarde, mais ne bloque pas, les autres threads TP en attente d’exécution. Si votre thread a besoin de passer beaucoup de temps parce qu’il bloque en permanence, il est préférable de créer un thread normal. Et si vous vous souciez vraiment que le thread ne soit pas retardé par le planificateur de TP, vous devez également utiliser un thread.

Le planificateur de TP a été modifié dans .NET 4.0 btw, ce que j’ai écrit n’est valable que pour les versions précédentes. Les bases sont toujours là, il utilise simplement un algorithme de planification plus intelligent. Basé sur un retour, planification dynamic en mesurant le débit. Cela ne compte vraiment que si vous avez beaucoup de discussions TP.

Deux raisons pas vraiment abordées:

  1. Le pool de threads est utilisé comme moyen habituel de gestion des fonctions de rappel d’E / S, qui sont généralement supposées se produire très rapidement après la fin de l’opération d’E / S associée. En général, la rapidité d’exécution est plus importante pour les tâches courtes que pour les tâches longues, mais les tâches longues du pool de threads retarderont l’exécution des tâches de notification qui auraient pu (et auraient dû) démarrer, s’exécuter et se terminer rapidement.
  2. Si une tâche de pool de threads est bloquée jusqu’à ce qu’une autre tâche de pool de tâches soit exécutée, il est possible que le thread se ralentisse, retardant ou, dans certains cas, bloquant complètement le début de cette tâche (ou de toute autre).

Généralement, avoir un thread threadpool acquérir un verrou (attendre si nécessaire) n’est pas un problème. S’il est nécessaire qu’un thread threadpool attend qu’un autre thread libère un verrou, le fait que ce dernier ait acquis le verrou en premier lieu implique qu’il a été démarré. En revanche, attendre par exemple que certaines données proviennent d’une connexion peut entraîner un blocage si une routine de rappel d’E / S est utilisée pour signaler l’arrivée de données. Si trop de threads de pool de threads attendent le rappel d’E / S pour signaler que des données sont arrivées, le système peut décider de différer le rappel jusqu’à la fin de l’un des threads.