J'ai toujours suivi le concept selon lequel le multithreading ne peut être mis en œuvre que sur un système à processeurs multiples où il y a plus d'un processeur à affecter à chaque thread et où chaque thread peut être exécuté simultanément. Il n'y a pas d'ordonnancement dans ce cas, car chaque thread dispose de ressources distinctes qui lui sont entièrement dédiées. Mais j'ai récemment lu quelque part que je pouvais aussi faire du multithreading sur un système à processeur unique. Est-ce exact ? Et si oui, quelle est la différence entre un système à processeur unique et un système à processeurs multiples ?
Réponses
Trop de publicités?Bien sûr, cela peut être fait sur un système à un seul processeur, et en fait c'est beaucoup plus facile de cette façon. Cela fonctionne de la même manière que l'exécution de plusieurs processus -- le noyau, par le biais d'une interruption de timer ou d'un autre mécanisme similaire, suspend l'un d'entre eux, en sauvegardant son état machine, et le remplace par l'état précédemment sauvegardé d'un autre -- la seule différence étant que deux threads du même processus partagent le même espace mémoire virtuel, ce qui rend le changement de tâche beaucoup plus efficace.
Le multithreading sur les systèmes multiprocesseurs est en fait beaucoup plus difficile, puisque vous avez des problèmes d'accès simultané à la mémoire de plusieurs cpus/cores, et tous les problèmes de synchronisation de la mémoire qui en découlent.
J'ai récemment lu quelque part que je pouvais faire du multithreading sur un seul système à processeur unique. Est-ce exact ? et si oui, quelle est la différence entre un système à processeur unique et un système à processeur unique ? différence entre un système à processeur unique et un système à processeurs multiples ?
Oui, vous pouvez faire du multithreading sur un système à processeur unique.
Dans un système multiprocesseur, plusieurs threads s'exécutent, simultanément sur différents cœurs. Par exemple, s'il y a deux threads et deux cœurs, alors chaque thread sera exécuté sur un cœur individuel.
Dans un système à processeur unique, plusieurs threads s'exécutent l'un après l'autre ou attendent qu'un thread se termine ou soit préempté par le système d'exploitation, en fonction de la priorité du thread et de la politique du système d'exploitation.
Comparaison des temps (exemple) :
si deux threads prennent 10us chacun pour s'exécuter, alors sur un système à 2 processeurs, le temps net pris est de 10us.
Si deux threads prennent 10us chacun pour s'exécuter, alors sur un système à 1 processeur, le temps net pris est de 20us.
Vous pouvez avoir plus de quatre threads actifs sur un système à quatre cœurs. Il y a est l'ordonnancement, sauf si vous pouvez garantir que les processus n'essaieront pas de créer plus de threads qu'il n'y a de processeurs.
Oui, vous pouvez avoir plusieurs threads sur un ordinateur à un seul cœur.
La différence entre les systèmes à processeur unique et les systèmes multiprocesseurs est qu'un système multiprocesseur peut effectivement faire plus d'une chose à la fois. Il peut faire N choses à la fois, où N est le nombre de cœurs de processeur. Un cœur de processeur unique ne peut faire qu'une seule chose à la fois. Comme le dit WhozCraig dans son commentaire, c'est la différence entre la concurrence réelle et la concurrence perçue.
Oui, vous pouvez tout à fait. Il y a longtemps (Win 95 ?), nous sommes passés du multitâche coopératif au multithreading, parce que quelqu'un se plantait toujours dans la partie coopérative. Chaque programme sur votre ordinateur a au moins un thread. Peut-être plus. Et le CPU passe d'un thread à l'autre comme un fou, quelques millions de fois par seconde. Si aucun d'entre eux n'a quelque chose à faire, il peut même rester inactif pendant un certain temps.
Les systèmes multicœurs signifient seulement que deux ou plusieurs de ces threads peuvent fonctionner en parallèle.
Cependant, cela vous rapporte beaucoup moins. Tout ce que vous pouvez faire avec le Multithreading sur une machine à un seul cœur est de simuler le Multitasking.
Le multitâche est suffisant pour empêcher le thread de l'interface graphique de se bloquer à cause d'une opération de longue durée. Cependant, il est généralement compliqué à mettre en œuvre, à moins que le compilateur ou le langage ne vous aide (comme C# async...await). En conséquence, de nombreux programmeurs d'interfaces graphiques ont simplement utilisé le multithreading et l'invocation pour simuler le multitâche. Le fait que le code s'exécute sur un seul ou plusieurs cœurs n'a aucune importance.
Plus important encore, le multitâche n'est PAS adapté aux opérations liées au CPU. Mais 95% de tous les problèmes asynchrones ne sont pas liés au CPU. Ils sont liés au réseau ou au disque. Sur un ordinateur à un seul cœur, le multithreading n'aide pas non plus à résoudre les problèmes liés au CPU. Si vous avez deux threads qui ont tous deux besoin de 100% de temps CPU (même programme ou programme différent) mais qu'il n'y a qu'un seul cœur pour les faire tourner, le CPU devra basculer entre les faire tourner à 49% et utiliser les 2% restants pour tous les autres threads qui ne font que peu de choses.
Enfin, seuls quelques problèmes peuvent réellement être multithreadés. Essayez simplement de multithreader la séquence de Fibonacci (un thread pour chaque paire) sans la rendre plus lente, plus gourmande en mémoire et plus complexe.
tl;dr ; Vous avez besoin de Multithreading et un ordinateur Multicore pour les problèmes liés au CPU. La plupart des problèmes asynchrones ne sont pas liés au CPU. Le multitâche est largement suffisant. Et vous pouvez tout à fait faire du multitâche en utilisant des threads, même sur une machine à un seul cœur.
Voici un exemple très simplifié. C'est en fait un prototype pour un programme que je suis en train de construire. C'est une implémentation du multitâche coopératif dans un seul thread.
main
définit simplement le quit
à false, et remplit un tableau de pointeurs de fonctions (les tâches), puis appelle loop
.
loop
utilise setjmp
pour définir un point de retour pour un saut non local (un saut out de la fonction à un emplacement précédent dans l'exécution) et procède ensuite à l'appel de la première tâche (fonction).
Chaque tâche se termine par yield()
. C'est-à-dire qu'aucune des fonctions de la tâche n'est réellement return
. Non seulement ils ne contiennent pas de return;
(ce qui serait parfait puisqu'ils sont void
fonctions, c'est-à-dire des procédures), mais ils n'atteindraient pas la return
même si elle était là parce que yield
revient à la setjmp
appel, cette fois-ci en donnant un 1 à l' if
déclaration dans loop
. La déclaration contrôlée par le if
sélectionne une autre tâche avant d'entrer à nouveau dans le système de gestion de l'information. while
boucle.
Ainsi, chaque fonction de tâche s'exécute plusieurs fois, cédant à la répartiteur (le if(setjmp...
) qui sélectionne une nouvelle tâche à exécuter.
#include <stdio.h>
#include <setjmp.h>
jmp_buf dispatch;
int ntasks;
void (*task[10])(void);
int quit;
void yield(void) {
longjmp(dispatch, 1);
}
void loop() {
static int i = 0;
if(setjmp(dispatch))
i = (i+1) % ntasks;
while(!quit)
task[i]();
}
int acc = 0;
void a(void) {
if (acc > 10) quit = 1;
printf("A\n");
yield();
}
void b(void) {
acc *= 2;
printf("B\n");
yield();
}
void c(void) {
acc += 1;
printf("C\n");
yield();
}
int main() {
quit = 0;
ntasks = 3;
task[0] = a;
task[1] = b;
task[2] = c;
loop();
return 0;
}
La différence entre cet exemple et un système informatique multitâche à processeur unique est que le processeur réel permet d'interrompre une tâche au milieu de son exécution et de la reprendre plus tard au même endroit. Cela n'est pas vraiment possible dans une simulation C où les tâches sont des fonctions uniques. Cependant, les tâches peuvent être composées d'une séquence de fonctions C qui renvoient chacune à un répartiteur (un tableau de pointeurs de fonctions, peut-être, ou une liste chaînée).
- Réponses précédentes
- Plus de réponses