Démystification : processus & threads

On trouve tout et son contraire sur ce sujet, que ce soit sur Internet, dans les discussions entre développeurs, et même sur des supports de cours. Clarifions un peu tout ça 🙂

Un processus désigne un programme en cours au sein du système d’exploitation. C’est ce qui est créé quand on lance un exécutable (typiquement, un fichier « .exe » dans le monde Windows). Un processus dispose de son propre espace mémoire, et en tant quel tel, il n’exécute aucune instruction.

Un thread est un fil d’exécution chargé de faire exécuter des instructions par un processeur. Il y a toujours un thread, souvent dénommé « principal », de créé lorsqu’un processus est créé. C’est ce thread de départ qui va commencer à exécuter le programme à partir de son point d’entrée (typiquement, une fonction « main » dans un programme console écrit en C/C++).

Maintenant que les bases sont posées, multithread, parallélisme, multiprocess… qu’en est-il de tout ça ? C’est parti 😉

La plus grande confusion autour du sujet est, à mon sens, le mélange entre les notions de concurrence et de parallélisme. On voit beaucoup l’abus de langage qui consiste à dire « j’ai plusieurs threads, c’est parallèle », mais c’est plus complexe que ça, et parfois faux.
Revenons un peu (beaucoup) en arrière. Je bidouille sur le PC sous Windows 95 de mon grand-père. J’ouvre Microsoft Word, le curseur clignote. J’ai le lecteur vidéo dans un coin sur lequel se lit en boucle une courte vidéo avec un singe perché sur un arbre. Je peux bouger le pointeur de la souris et naviguer dans l’explorateur de fichiers. La machine semble faire tout ça « en parallèle », n’est-ce pas ?

Pourtant, ce vieux coucou était équipé d’un processeur unique, avec un et un seul fil d’exécution.
Dans les années 2000 sortaient encore des processeurs flambants neufs et qui n’étaient pas encore à coeurs multiples, et pourtant, on pouvait les utiliser pour écouter de la musique tout en prenant des notes et en surveillant l’heure dans un coin de l’écran.
C’est parce que le processeur passe de processus en processus, et de thread en thread, leur accordant un peu de son temps, exécutant les prochaines instructions qu’ils souhaitent réaliser, avant de les mettre en pause et passer à quelqu’un d’autre. Quand un thread est mis en pause par le système d’exploitation, on dit qu’il est préempté. Le passage d’un thread à un autre est quant à lui appelé context switch.
Cette alternance se fait à une vitesse très élevée pour un être humain : on parle là de quelques millisecondes, tout au plus. Mais c’est ce qui permet à la machine de faire fonctionner plusieurs programmes, et pour nous êtres humains, cela semble se faire « en même temps ». En réalité la machine accorde un peu de son temps à chacun, basculant entre eux à toute vitesse.
Sous Windows, c’est le scheduler qui a ce rôle.

C’est l’aspect concurrentiel des threads. Sur un ordinateur classique, de par le grand nombre de variables impliquées, il est virtuellement impossible d’anticiper dans quel ordre ils seront ordonnancés. Dans des projets de l’embarqué, au contraire, ils sont généralement ordonnancés d’une manière strictement prévue à l’avance.

Donc, nous y voilà : un processeur peut travailler sur plusieurs tâches en basculant entre elles, et en calculant petit bout par petit bout. Du point de vue d’un être humain, on peut réaliser plusieurs choses « en même temps », et pourtant il s’agit seulement de concurrentiel et non de parallèle.

Et que se passerait-il si nous avions plusieurs processeurs (c’est possible avec les carte-mères multi-socket) ou un seul processeur divisé en plusieurs « cœurs » ? Eh bien, chaque processeur serait capable, de la même façon, de basculer entre des tâches différentes petit bout par petit bout… sauf qu’à un instant T donné, chacun serait en train d’exécuter une instruction sur sa tâche active. Et ça, c’est du parallèle.

Maintenant que cette nuance est clarifiée, regardons de plus prêt les processus et les threads. Mis à part des cas très spécifiques comme par exemple l’interpréteur CPython (shame!), le parallélisme peut être atteint en utilisant des processus comme en utilisant des threads. Quelle option choisir dans ce cas ?
D’une manière générale, privilégiez toujours les threads. Les threads sont lancés au sein d’une processus unique et peuvent non seulement communiquer entre eux mais aussi accéder au même espace en mémoire sans faire de pirouettes.
Les processus, quant à eux, sont indépendants, sont plus longs à lancer (il s’agit pour rappel de lancer un exécutable), et ont leur propre espace mémoire. Il est possible de communiquer entre processus, mais cela se fait indirectement par le biais de mécanismes fournis par le système d’exploitation.

Alors, maintenant, il faudrait peut-être parler de SIMD…