Le Gestionnaire des tâches indique que le système fonctionne avec plus d'un millier de threads

17

J'ai ouvert le Gestionnaire des tâches et j'ai regardé sous la zone "Système" et j'ai vu:

Fils: 1337

Étant donné que j'ai un processeur double cœur avec hyper-threading disponible (c'est-à-dire quatre threads), comment est-il possible d'avoir plus de 1000 threads alors que mon processeur n'est censé en avoir que quatre?

dpl47
la source
3
N'est-ce pas pour cela qu'ils appellent ça de l' hyper- lecture? :)
un CVn
9
Gee, ont-ils enfin inventé la "multiprogrammation" ??? (
Daniel R Hicks
10
Quelqu'un vient-il de changer le nombre de 1337?
Erty Seidohl
11
Comment une entreprise avec quatre bureaux peut-elle avoir 1337 employés? Facile; les employés se relaient à l'aide des bureaux.
Eric Lippert

Réponses:

50

La réponse simple est que tous les threads ne s'exécutent pas simultanément. Pour une explication plus complète, lisez la suite.

Le planificateur de tâches du système d'exploitation est généralement conçu pour planifier des applications, ce qui vous permet d'effectuer une tâche pendant que l'ordinateur travaille sur une autre. Autrefois, le test décisif du multitâche formait une disquette tout en faisant autre chose. Si vous vouliez vraiment tester le système d'exploitation, vous formateriez une disquette tout en téléchargeant un fichier via un modem connecté au port série. Comme le matériel est devenu suffisamment puissant pour le faire de manière significative, la lecture vidéo figurait parfois également dans de tels tests. Si le planificateur de tâches du système d'exploitation peut gérer l'exécution de ces tâches sans problème, il peut tout gérer.

Cependant, le planificateur de tâches ne planifie pas réellement les applications (processus), il planifie les threads . Chaque application a au moins un thread, mais peut potentiellement utiliser un grand nombre de threads pour diviser le travail effectué en parties liées ou indépendantes. Par exemple, il est courant pour une application d'avoir un thread qui gère l'interface utilisateur et de créer un autre thread lorsque l'utilisateur lance une opération potentiellement longue (ce qui pourrait être des choses comme l'impression, le recalcul d'une feuille de calcul, un environnement de développement faisant une recherche de symboles, etc. etc.). Certains environnements de programmation introduisent une certaine quantité de threads de manière invisible pour le programmeur; par exemple, Java et .NET peuvent effectuer une récupération de placedans un thread séparé, qui est hors du contrôle immédiat du programmeur. Certains programmes créent un certain nombre de threads au début et les regroupent, car la création de nouveaux threads est une opération relativement coûteuse (vous ne devez donc pas nécessairement avoir à créer un thread à chaque fois que vous en avez besoin). Tout ce qui fait un aperçu est généralement effectué dans un thread séparé, de sorte que le reste de l'interface utilisateur reste réactif pendant la génération de l'aperçu. Etc. Pris ensemble, tout cela signifie que le nombre de threads sur le système à tout moment peut facilement être plusieurs fois le nombre de processus.

Chaque thread peut être dans l'un des quelques états possibles, mais la distinction la plus importante est entre les états en cours d'exécution , exécutable et en attente ; la terminologie peut différer légèrement, mais c'est l'idée générale. À tout moment, un seul thread par processeur virtuel (en raison de l'hyperthreading et de technologies similaires) peut être en cours d'exécution (c'est-à-dire, exécuter des instructions de code machine), mais un nombre illimité de threads peuvent être exécutables (ce qui signifie qu'il est possible d'obtenir la CPU la prochaine fois que le planificateur doit décider quel thread doit être autorisé à s'exécuter). Attendre (également appelés bloqués), les threads ne sont que cela, en attente de quelque chose - les cas les plus courants sont probablement qu'ils attendent des E / S utilisateur, disque ou réseau (l'entrée utilisateur en particulier est exceptionnellement lente).

Le nombre de threads que vous voyez dans le gestionnaire de tâches est le nombre total de threads dans l'un de ces états. Par exemple, le système Windows 7 sur lequel je tape ceci a actuellement environ 70 processus démarrés mais près de 900 threads. Avec tous les processus d'arrière-plan pour gérer diverses tâches et la façon dont ils sont probablement subdivisés en une multitude de threads chacun, ce n'est pas un nombre scandaleux.

Aller un peu plus loin dans les profondeurs de l'implémentation technique, au cœur même du planificateur de tâches d'un système d'exploitation multitâche préventif se trouve généralement une sorte de crochet d'interruption matérielle. Cela signifie que le noyau peut arrêter le CPU lorsqu'il n'a pas de travail utile à effectuer (c'est presque certainement l'une des raisons, sinon la raison, pour laquelle Linux vérifie les HLTinstructions au démarrage sur IA-32-CPU compatibles, et effectue probablement des vérifications similaires sur d'autres architectures), en sachant que, à un moment futur raisonnablement déterminé, une interruption se déclenchera et le planificateur de tâches sera invoqué. Étant donné que l'interruption se déclenche quel que soit le travail effectué par le processeur (c'est l'idée derrière les interruptions), le planificateur est exécuté régulièrement et a la possibilité de déterminer quel thread doit être exécuté pendant la tranche de temps suivante. Étant donné que les commutateurs de contexte sont relativement chers, il est généralement possible (au moins via le code source) de régler l'agressivité avec laquelle le planificateur bascule entre les threads; la commutation des threads rend le système plus réactif, mais la surcharge de commutation signifie que le temps global pour terminer un ensemble de tâches donné est plus long. Le plus rapideLe système sera celui qui bascule entre les threads uniquement lorsque le thread en cours d'exécution n'est plus possible de s'exécuter (ce qui signifie qu'il est bloqué en attente de quelque chose ou qu'il a terminé son travail) car cela minimise la surcharge, tandis que le système le plus réactif basculera entre les threads. chaque fois que le planificateur est appelé car cela minimise le temps moyen d'attente avant qu'un thread particulier n'ait le temps CPU. Le paramètre idéal se situe généralement quelque part entre ces deux, et le compromis entre ces choix est probablement l'une des principales raisons pour lesquelles Linux propose plusieurs programmateurs ainsi que certains paramètres de réglage via la configuration du noyau.

D'un autre côté, les systèmes d'exploitation et les environnements multitâches coopératifs ( Windows 3.x en est un exemple), s'appuient sur chaque application pour céder régulièrement le contrôle au planificateur. Il y a généralement une fonction API spécifiquement conçue pour cela, et souvent de nombreuses fonctions API le feront dans le cadre de leur flux d'exécution interne, car cela aide à rendre l'expérience utilisateur plus fluide. Cette approche de conception fonctionne bien tant que toutes les applications se comportent bien et cèdent le contrôle à de courts intervalles au cours de toutes les opérations de longue durée (longue durée signifiant plus qu'une petite fraction de seconde), mais une application qui ne peut pas se boucher l'ensemble du système. C'est une des principales raisons pour lesquelles Windows 3.x a si mal fait le test multitâche que j'ai mentionné ci-dessus, tandis que OS / 2se promenait gaiement tout en effectuant les mêmes tâches sur le même matériel: une application pouvait dire au lecteur de disquette d'écrire un certain secteur, et le temps qu'il fallait pour le faire avant l'appel renvoyé pouvait en fait être mesurable (des dizaines à des centaines de millisecondes ou plus); un système multitâche préemptif verrait son ordonnanceur s'introduire lors de sa prochaine invocation planifiée, notez que le thread qui est actuellement "en cours d'exécution" est en fait bloqué par l'appel d'écriture et basculez simplement vers un autre thread exécutable. (En pratique, c'est un peu plus impliqué, mais c'est l'idée générale.)

Dans des environnements à la fois préventifs et multitâches, il existe également la possibilité que différents threads aient des priorités différentes. Par exemple, il est probablement plus important d'exécuter en temps opportun le thread qui reçoit des données sur une liaison de communication que celui qui met à jour l'affichage de l'heure système, donc le thread de réception a une priorité élevée et le thread de mise à jour de l'affichage du temps a une priorité faible . Les priorités de thread jouent un rôle dans la décision de l'ordonnanceur de quel thread autoriser l'exécution (par exemple, très simplifié, les threads de haute priorité doivent toujours s'exécuter avant les threads de faible priorité, donc même si le thread de basse priorité a encore du travail à faire, si le thread de haute priorité devient exécutable, il a priorité), mais ces décisions de planification spécifiques n'affectent pas la conception du mécanisme sous-jacent.

un CVn
la source
11
J'adore "la saisie des utilisateurs en particulier est exceptionnellement lente"
John Dvorak
Les "threads" qu'un CPU "a" se réfèrent au nombre qu'il peut exécuter simultanément à un moment donné. Il bascule entre les threads actifs, donnant à chacun un tour, ou une part du CPU pour une tranche de temps, lorsque cela est possible. Les processus / threads "bloqués" ou en attente d'E / S (comme les entrées disque, clavier / souris, etc.) ou autre chose (comme les primitives de synchronisation comme les mutex, etc.) sautent leur tour.
LawrenceC
1
Les commentaires sont agréables à demander des éclaircissements, mais ils ne conviennent pas pour une discussion approfondie, et à un moment donné deviennent difficiles à lire. Pouvez-vous plutôt apporter ceci à Super User Chat ? Je vous remercie.
slhck
1
@slhck J'ai créé une salle, mais je n'ai trouvé aucun moyen de migrer la discussion dans ces commentaires, ce qui serait bien. Est-ce quelque chose que vous pouvez faire manuellement en tant que modérateur? chat.stackexchange.com/rooms/9547/…
un CVn le
1
Malheureusement non. Il existe un processus de migration automatique qui ne peut pas être déclenché manuellement, mais nous ne pouvons pas déplacer les commentaires vers une salle de chat. Nous laisserons les commentaires ici pour le moment, mais j'encourage les autres à poursuivre la discussion dans la salle de chat que vous avez créée.
slhck
18

Pensez à une autoroute à quatre voies avec 1037 véhicules.

Votre système d'exploitation a besoin de nombreux processus en cours d'exécution pour fonctionner avec de nombreux services. Même les programmes graphiques les plus simples nécessiteront une programmation multithread. Lorsque vous pensez à votre lot de programmes ouverts, vous voyez qu'il est nécessaire de partager les ressources de puissance de calcul.

Ce que votre gestionnaire de tâches affiche est la charge actuelle du système. Ce que vos spécifications comp montrent, c'est combien de threads (en frontend) sont acceptés pour une exécution parallèle. Sans entrer beaucoup dans la différence entre les fonctionnalités d'hyperthreading et multicœurs, avec un thread frontal plus logique acceptant, un système fonctionnera généralement mieux.

174140
la source
8
"Même les programmes graphiques les plus simples nécessiteront une programmation multithread." Faux. Il est parfaitement possible d'écrire des applications GUI monothread; jusqu'à Windows 95, à toutes fins utiles, tout le monde l'a fait de cette façon. Cela rend certaines tâches plus compliquées (par exemple, l'impression en arrière-plan est triviale avec plusieurs threads mais décidément non triviale dans une application à un seul thread, en particulier si vous êtes également limité en mémoire comme c'était le cas à l'époque), mais il y a une énorme différence entre " X est facilité par Y "et" X nécessite Y ".
un CVn du
8
@ MichaelKjörling: "jusqu'à Windows 95, à toutes fins utiles, tout le monde l'a fait de cette façon" * - vraiment? Même sur les systèmes * nix exécutant Motif dans les années 80?
LarsH
@LarsH Bon point, et j'ai pensé trop tard pour le modifier dans le commentaire. Mais cela ne nie pas le point: à savoir, qu'il est parfaitement possible d'écrire des applications GUI à un seul thread. Vous n'avez pas besoin de multithreading pour cela, bien que cela facilite (considérablement) certaines tâches sur le programmeur.
un CVn du
@ MichaelKjörling: Je suis d'accord, cela ne nie pas votre point, qui est valable. (Je ne pense pas que la déclaration incorrecte de uprego nie son argument non plus.)
LarsH
Comme exemple de ce qu'a dit @ MichaelKjörling, Visual Basic (avant .NET) en tant que langage de programmation n'avait pas de support multi-threading. Tout a été exécuté sur un seul thread. Si vous vouliez traiter l'entrée utilisateur au milieu d'une opération de longue durée, vous appelleriez DoEvents, qui traiterait la file d'attente de messages - mais cela a été fait sur le même thread et bloquerait cette opération de longue durée jusqu'à ce que tous les messages aient été traités . (Bien sûr, vous pouvez appeler les fonctions de l'API Win32 et / ou créer des processus supplémentaires, mais à ce stade, vous pouvez également utiliser l'un des langages de niveau inférieur.)
Bob
5

Nous devons prendre du recul et nous demander: comment un ordinateur avec un seul processeur peut-il avoir deux threads?

Les threads sont des entités logicielles, pas du matériel. Pour avoir un autre thread, vous avez juste besoin de mémoire pour les objets qui composent le thread, comme une structure de descripteur et une pile.

Le système d'exploitation bascule entre les threads à différents moments, comme à l'intérieur de certaines interruptions (comme une interruption de minuterie) ou lorsque des threads appellent le système d'exploitation.

De tous les threads qui existent dans le système, seul un sous-ensemble est généralement dans un état qui est communément appelé "exécutable". Les threads exécutables sont impatients de s'exécuter: ils sont soit en cours d'exécution, soit assis dans une "file d'attente d'exécution", en attente d'être envoyés par le planificateur. Les threads qui ne sont pas exécutables sont "bloqués", en attente d'acquérir une ressource ou de recevoir une entrée, ou "en veille", c'est comme être bloqués en entrée, où l '"entrée" est le passage du temps. Un «changement de contexte» a lieu lorsque la fonction du planificateur dans le système d'exploitation examine la file d'attente d'exécution d'un processeur et choisit un thread différent à exécuter.

Ne soyez pas confondu par "hyperthreading" , qui est le nom d'Intel pour une fonctionnalité matérielle particulière.

Kaz
la source