J'essaie de comprendre le multi-threading en c ++, mais je suis coincé dans ce problème: si je lance des threads dans une boucle for, ils affichent des valeurs incorrectes. Voici le code:
#include <iostream>
#include <list>
#include <thread>
void print_id(int id){
printf("Hello from thread %d\n", id);
}
int main() {
int n=5;
std::list<std::thread> threads={};
for(int i=0; i<n; i++ ){
threads.emplace_back(std::thread([&](){ print_id(i); }));
}
for(auto& t: threads){
t.join();
}
return 0;
}
Je m'attendais à obtenir imprimé les valeurs 0,1,2,3,4 mais j'ai souvent obtenu la même valeur deux fois. Voici la sortie:
Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5
Qu'est-ce qui me manque?
c++
multithreading
Ermando
la source
la source
i
par la valeur lambda,[i]
.emplace_back
est étrange:emplace_back
prend une liste d'arguments et la transmet à un constructeur pourstd::thread
. Vous avez passé une instance (rvalue) destd::thread
, donc vous allez construire un thread, puis déplacer ce thread dans le vecteur. Cette opération est mieux exprimée par la méthode la plus courantepush_back
. Il serait plus judicieux d'écrirethreads.emplace_back([i](){ print_id(i); });
(construire sur place) outhreads.push_back(std::thread([i](){ print_id(i); }));
(construire + déplacer) qui sont un peu plus idiomatiques.Réponses:
La
[&]
syntaxe provoquei
la capture par référence . Par conséquent, très souventi
, la progression du thread sera plus avancée que prévu. Plus sérieusement, le comportement de votre code n'est pas défini s'ili
sort de la portée avant l'exécution d'un thread.Capture
i
par valeur - c'est à direstd::thread([i](){ print_id(i); })
est le correctif.la source
std::thread([=](){ print_id(i); })
i
avec l'écriture du thread principal et les autres threads en lecture.Deux problèmes:
Vous n'avez aucun contrôle sur le moment où le thread s'exécute, ce qui signifie que la valeur de la variable
i
dans le lambda peut ne pas être celle que vous attendez.La variable
i
est locale pour la boucle et la boucle uniquement. Si la boucle se termine avant l'exécution d'un ou de plusieurs threads, ces threads auront une référence non valide à une variable dont la durée de vie est terminée.Vous pouvez résoudre ces deux problèmes très simplement en capturant la variable
i
par valeur plutôt que par référence. Cela signifie que chaque thread aura une copie de la valeur, et cette copie sera effectuée uniquement pour chaque thread.la source
Autre chose:
n'attendez pas d'avoir toujours une séquence ordonnée: 0, 1, 2, 3, ... car le mode d'exécution multithreading a une spécificité: l' indéterminisme .
L'indéterminisme signifie que l'exécution du même programme, dans les mêmes conditions, donne un résultat différent.
Cela est dû au fait que l'OS planifie les threads différemment d'une exécution à l'autre en fonction de plusieurs paramètres: charge CPU, priorité des autres processus, interruptions éventuelles du système, ...
Votre exemple ne contient que 5 threads, donc c'est simple, essayez d'augmenter le nombre de threads, et par exemple mettez un sommeil dans la fonction de traitement, vous verrez que le résultat peut être différent d'une exécution à l'autre.
la source