Si vous parlez de threads pris en charge par le matériel (l'Hyperthreading d'Intel, par exemple), vous pouvez consulter l'architecture de la puce spécifique que vous exécutez. Ou, si vous connaissez le nombre de cœurs physiques, les outils du système d'exploitation peuvent généralement révéler le nombre de cœurs virtuels dans le système. Par exemple, le gestionnaire de tâches de Windows sur mon ordinateur portable ici présent affichera huit processeurs, mais je n'ai en fait que quatre cœurs, chacun d'entre eux ayant deux threads matériels.
Ce n'est certainement pas la seule façon de procéder. Et il existe des processeurs comportant jusqu'à huit threads matériels (réponse de Dave Haynie's à la question Il est possible qu'un cœur de CPU ait trois ou quatre threads et comment cela affecte-t-il les performances ?). Mais vous n'avez pas tellement de chances de les encouter, sauf si vous travaillez dans un centre de données.
En ce qui concerne les threads logiciels, chaque CPU [virtuel] en possède un. Le multithreading logiciel est un multiplexage par répartition dans le temps d'une ressource du CPU. Un thread s'exécute pendant un certain temps, puis il y a un événement - une attente sur une ressource d'E/S, un minuteur au niveau du système - qui signale un échange de tâches. Le système d'exploitation consulte la file d'attente des tâches en attente pour connaître les priorités et planifie les N threads suivants, un par cœur virtuel. Mais tant que le thread n'est pas réellement en cours d'exécution, il existe en tant que morceau de mémoire quelque part, il n'est "sur" aucun cœur.
Il est possible, mais rare, qu'un thread se voie spécifiquement attribuer une "affinité" pour un cœur spécifique dans un système. Un exemple serait une machine virtuelle - il y a de très bonnes raisons d'affecter une VM à un seul cœur. Certaines architectures informatiques multitraitement ne sont pas totalement symétriques. Par exemple, les premiers systèmes multitraitement Macintosh n'avaient qu'une seule unité centrale capable de gérer les interruptions. Ainsi, tout le traitement des interruptions devait se faire sur cette seule unité centrale, quel que soit le nombre de cœurs dont vous disposiez réellement.
En termes de logiciels, il existe d'autres raisons. Lorsque vous dites "thread" dans un logiciel, vous parlez généralement de la plus petite unité d'exécution du code. Un thread ne traite à peu près que du contexte de l'unité centrale elle-même. Contrairement au "processus", qui inclut la gestion des ressources et le suivi des allocations d'E/S, les contextes MMU, etc. Je peux écrire une application multi-processus sur les systèmes UNIX en utilisant la fonction fork(), qui est vraiment facile à utiliser (elle divise un processus en deux processus), mais qui utilise beaucoup de ressources. Je peux utiliser des threads sur la plupart des systèmes d'exploitation, qui sont beaucoup moins gourmands en ressources. Cependant, chaque thread d'un programme s'exécute dans le même contexte de processus. Un planificateur intelligent peut donc s'en rendre compte lors du prochain cycle de planification et placer les nouveaux threads dans le même contexte de processus, ce qui signifie le même CPU. Aucune exigence pour cela, juste une optimisation pour réduire l'overhead de l'ordonnancement.