La plupart des réponses ci-dessus parlent de performances et de fonctionnement simultané. Je vais aborder cela sous un angle différent.
Prenons le cas, disons, d'un programme d'émulation de terminal simpliste. Vous devez faire les choses suivantes:
- surveillez les caractères entrants du système distant et affichez-les
- surveillez les éléments provenant du clavier et envoyez-les au système distant
(Les vrais émulateurs de terminal font plus, y compris potentiellement l'écho des éléments que vous tapez sur l'écran, mais nous passerons là-dessus pour le moment.)
Maintenant, la boucle de lecture à partir de la télécommande est simple, selon le pseudocode suivant:
while get-character-from-remote:
print-to-screen character
La boucle de surveillance du clavier et d'envoi est également simple:
while get-character-from-keyboard:
send-to-remote character
Le problème, cependant, est que vous devez le faire simultanément. Le code doit maintenant ressembler davantage à ceci si vous n'avez pas de thread:
loop:
check-for-remote-character
if remote-character-is-ready:
print-to-screen character
check-for-keyboard-entry
if keyboard-is-ready:
send-to-remote character
La logique, même dans cet exemple délibérément simplifié qui ne prend pas en compte la complexité réelle des communications, est assez obscure. Avec le threading, cependant, même sur un seul noyau, les deux boucles de pseudo-code peuvent exister indépendamment sans entrelacer leur logique. Étant donné que les deux threads seront principalement liés aux E / S, ils ne mettent pas une charge lourde sur le processeur, même s'ils sont, à proprement parler, plus de gaspillage de ressources processeur que la boucle intégrée.
Maintenant, bien sûr, l'utilisation dans le monde réel est plus compliquée que ce qui précède. Mais la complexité de la boucle intégrée augmente de façon exponentielle à mesure que vous ajoutez plus de préoccupations à l'application. La logique est de plus en plus fragmentée et vous devez commencer à utiliser des techniques telles que les machines à états, les coroutines, etc. pour que les choses soient gérables. Gérable, mais non lisible. Le filetage garde le code plus lisible.
Alors pourquoi n'utiliseriez-vous pas le filetage?
Eh bien, si vos tâches sont liées au processeur au lieu des E / S, le threading ralentit en fait votre système. La performance en souffrira. Beaucoup, dans de nombreux cas. ("Thrashing" est un problème courant si vous supprimez trop de threads liés au processeur. Vous finissez par passer plus de temps à changer les threads actifs qu'à exécuter le contenu des threads eux-mêmes.) En outre, l'une des raisons pour lesquelles la logique ci-dessus est si simple est que j'ai choisi très délibérément un exemple simpliste (et irréaliste). Si vous vouliez faire écho à ce qui a été tapé à l'écran, vous avez un nouveau monde de blessures lorsque vous introduisez le verrouillage des ressources partagées. Avec une seule ressource partagée, ce n'est pas tellement un problème, mais cela commence à devenir un problème de plus en plus important car vous avez plus de ressources à partager.
Donc, en fin de compte, le filetage concerne beaucoup de choses. Par exemple, il s'agit de rendre les processus liés aux E / S plus réactifs (même s'ils sont globalement moins efficaces) comme certains l'ont déjà dit. Il s'agit également de rendre la logique plus facile à suivre (mais uniquement si vous minimisez l'état partagé). Il s'agit de beaucoup de choses et vous devez décider au cas par cas si ses avantages l'emportent sur ses inconvénients.