C ++ Threads inside for loop print false values


19

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?


7
Passez ipar la valeur lambda, [i].
rafix07

1
Il convient de noter que votre utilisation de emplace_backest étrange: emplace_backprend une liste d'arguments et la transmet à un constructeur pour std::thread. Vous avez passé une instance (rvalue) de std::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 courante push_back. Il serait plus judicieux d'écrire threads.emplace_back([i](){ print_id(i); });(construire sur place) ou threads.push_back(std::thread([i](){ print_id(i); }));(construire + déplacer) qui sont un peu plus idiomatiques.
Milo Brandt

Réponses:


17

La [&]syntaxe provoque ila capture par référence . Par conséquent, très souvent i, 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'il isort de la portée avant l'exécution d'un thread.

Capture ipar valeur - c'est à dire std::thread([i](){ print_id(i); })est le correctif.


2
Ou moins utilisé et pas souvent conseilléstd::thread([=](){ print_id(i); })
Wander3r

3
Le comportement est déjà indéfini car il s'agit d'une course de données sur le (non atomique) iavec l'écriture du thread principal et les autres threads en lecture.
noyer

6

Deux problèmes:

  1. Vous n'avez aucun contrôle sur le moment où le thread s'exécute, ce qui signifie que la valeur de la variable idans le lambda peut ne pas être celle que vous attendez.

  2. La variable iest 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.


5

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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.