Prévenir la famine des threads – meilleures pratiques

la programmation


Je recherche plus d’informations sur la façon d’éviter la famine/le blocage/la course aux threads dans une application ou un service qui pourrait éventuellement exécuter jusqu’à 200 threads simultanément sur plusieurs processeurs/cœurs.

La plupart des exemples que j’ai trouvés en ligne montrent comment coordonner entre 3 ou 4 threads maximum et je prévois une application qui en aura des centaines.

J’ai créé des applications qui ont exécuté jusqu’à 30 threads, mais au-delà de cela, je m’inquiète du fait que certains threads n’obtiennent pas de temps CPU, sans parler de la synchronisation.

Où puis-je trouver de très bons articles et blogs ?
Quels sont les meilleurs livres sur le sujet ?
Il existe de nombreux livres et articles sur le sujet, je recherche les plus informatifs et pédagogiques mais non présentés dans un langage d’ingénierie au-dessus de ma compréhension.

Toutes les suggestions sont appréciées.

Solution 1

Il y a trop de scénarios à couvrir, il est donc impossible de répondre à votre question.

Mais utiliser plus de threads actifs que le nombre de cœurs est généralement contre-productif. Vous pouvez bénéficier de l’utilisation de plus de threads que le nombre de cœurs uniquement si la plupart des threads veulent généralement dormir, par exemple en attendant certaines opérations d’E/S comme l’appel recv() qui reçoit des données du réseau. Même dans ce cas, vous souhaitez contrôler activement le nombre de threads qui s’exécutent en parallèle, donc démarrer 200 threads en espérant que seulement 4 d’entre eux *voudront* s’exécuter en parallèle si vous avez 4 cœurs n’est pas une bonne pratique.

Si vous devez exécuter 200 tâches parallèles actives en permanence, il est préférable de créer uniquement le même nombre de threads que le nombre de cœurs et de diviser les tâches en petits travaux. Vous exécutez ces petits travaux sur vos threads dans l’ordre de votre choix en les jetant dans une file d’attente de travaux d’où vos threads les extraient un par un. C’est déjà proche d’avoir votre propre planificateur. Une technique qui aboutit souvent à une solution plus sophistiquée que la précédente consiste à utiliser des threads/coroutines verts (API POSIX ucontext obsolète sur certaines distributions Linux et API fibre sur Windows).

En utilisant les threads verts, vous pouvez diviser un thread en plusieurs threads verts et vous effectuez les tâches de commutation/coopération entre ces threads verts explicitement dans l’espace utilisateur. Disons que vous avez 4 cœurs. Ensuite, vous pouvez démarrer 4 threads et si vous divisez chaque thread en 100 threads verts, vous avez 400 threads verts mais seulement 4 d’entre eux s’exécutent en parallèle à un moment donné et vous pouvez écrire votre propre planificateur car vous avez le contrôle sur la tâche basculant entre les fils verts.

Écrire par exemple un serveur Web utilisant des threads verts présente l’avantage suivant : le code pour les implémentations multithread normales et asynchrones/threads verts peut utiliser le même code de servlet s’il est bien écrit (car vous pouvez placer la tâche de commutation dans votre propre implémentation sinon les appels de fonction de blocage comme les fonctions de lecture/écriture de socket et l’implémentation des mêmes appels de fonction de blocage en mode multithread appellent en fait les fonctions équivalentes du système d’exploitation bloquant).

コメント

タイトルとURLをコピーしました