Lorsque vous dormez un fil, que se passe-t-il réellement?
Je vois que dormir un thread "met en pause le thread actuel pendant une période de temps donnée" . Mais comment ça marche?
Selon comment Thread.sleep () fonctionne en interne et comment Thread.sleep fonctionne-t-il vraiment? :
- la durée du sommeil sera soumise à une granularité spécifique au système
- le sommeil bloque
- le thread quitte le CPU et arrête son exécution
- le thread ne consomme pas de temps CPU pendant le sommeil
Je ne comprends tout simplement pas la mécanique interne et fondamentale de ce que tout cela signifie.
Je comprends qu'il existe quelque chose appelé le planificateur qui est responsable de la commutation entre les threads.
Les sources semblent indiquer que cela varie selon le système d'exploitation (ou le matériel?) Et la plupart des threads disposent de 1 à 60 ms environ pour effectuer certaines actions avant que le processeur ne passe à un autre thread.
Mais lorsqu'un fil dort (par exemple, plusieurs secondes), comment reprend-il? Je suppose qu'une minuterie est impliquée d'une manière ou d'une autre, est-ce l'horloge de la carte mère? Est-ce lié à la fréquence d'horloge du processeur?
Et même si un minuteur est impliqué, comment le CPU sait-il quand il est temps de prêter attention au thread? Ne devrait-il pas constamment vérifier le fil pour voir s'il est prêt? Est -ce pas efficace vote et donc de genre est beaucoup de temps CPU?
Le sommeil est-il spécifique à la langue du thread ou le système d'exploitation en est-il responsable ou est-ce une chose spécifique au CPU?
Quelqu'un pourrait-il m'expliquer cela avec des explications de base sur des choses comme le planificateur et ce que le CPU fait pendant tout cela?
la source
Réponses:
L'exécution d'un programme est beaucoup plus impliquée que le simple code de ce programme.
Tout programme qui s'exécute dans un système d'exploitation multi-processus est sous le contrôle du planificateur du système d'exploitation , et le planificateur maintient une table explicite indiquant quel processus est en cours d'exécution, ceux qui attendent de s'exécuter lorsque les cycles du processeur sont disponibles et ceux qui ne sont même pas essayer de courir (dormir). Le planificateur attribue généralement des tranches de temps de taille égale aux processus en fonction de leur priorité et de leur historique d'exécution. En fin de compte, cette boucle est entraînée par des interruptions matérielles, généralement générées par un oscillateur sur la carte principale.
La mise en veille est toujours une fonctionnalité qu'un langage de programmation ne peut prendre en charge que parce que l'environnement d'exécution où il sera exécuté le prend en charge. Un programme normal ne peut pas suspendre lui - même , il ne peut dire le planificateur comment il aime à traiter - et le planificateur est nullement obligé ou même toujours capables de satisfaire ce souhait. Pensez à un ordinateur portable fermé et en veille prolongée; l'oscillateur de la carte principale continue de pulser, mais comme le planificateur n'est pas en cours d'exécution, aucun processus ne peut être exécuté, quelle que soit sa priorité.
la source
Comme Doc Brown l'a mentionné dans un commentaire, les interruptions sont la clé, et pas seulement pour dormir.
Une interruption est un signal matériel que le processeur doit arrêter ce qu'il fait et exécuter un morceau de code. Les périphériques externes déclenchent des interruptions lorsqu'ils nécessitent l'attention du processeur: par exemple, lorsqu'un disque a fini de lire les données, ou qu'une touche a été enfoncée, ou qu'un compte à rebours sur la carte mère a atteint zéro.
Le code de gestion des interruptions est généralement très petit et très rapide. Par exemple, lorsque le disque indique qu'un bloc a été copié en mémoire, le système d'exploitation peut simplement enregistrer ce fait dans une liste de "blocs prêts" quelque part, puis revenir à tout ce qu'il faisait. Vous ne voulez pas que le CPU passe tout son temps dans le code de gestion des interruptions et n'exécute pas le code utilisateur.
Un élément de code piloté par interruption qui n'est pas nécessairement petit est le planificateur. Il est déclenché par un signal provenant d'un compte à rebours et examine l'état du système chaque fois qu'il est exécuté. Cela comprend généralement l'identification des processus prêts à être exécutés (par exemple, parce que le bloc qu'ils attendaient est arrivé en mémoire), ainsi que ceux qui ont épuisé leur tranche de temps.
Ainsi, lorsque vous exécutez une veille de thread, ce que vous faites est de dire au système d'exploitation que (1) vous abandonnez votre tranche de temps, et (2) vous ne devriez pas être réveillé à nouveau jusqu'à ce qu'un certain temps se soit écoulé.
Chaque fois que le planificateur s'exécute, il examine votre thread et le marque comme «prêt à fonctionner» uniquement si ce temps s'est écoulé. C'est une interrogation, en quelque sorte, mais ce n'est pas une interrogation "en boucle occupée" car elle est déclenchée par une interruption. Ce n'est pas non plus si cher: généralement, il n'y a qu'un millier de threads à la fois.
Cela devrait également vous donner une idée des raisons pour lesquelles les temps de veille ne sont pas exacts: lorsque votre thread est prêt à fonctionner, il peut y avoir d'autres threads qui sont toujours en cours d'exécution et n'ont pas épuisé leur tranche de temps. Ou il peut y avoir des threads de priorité supérieure qui sont prêts à s'exécuter.
la source