c ++ Discussioni all'interno per loop che stampano valori errati


19

Sto cercando di capire il multi-threading in c ++, ma sono bloccato in questo problema: se lancio i thread in un ciclo for, stampano valori errati. Questo è il codice:

#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;
}

Mi aspettavo di stampare i valori 0,1,2,3,4 ma spesso ottenevo lo stesso valore due volte. Questo è l'output:

Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5

Cosa mi manca


7
Passare iper valore di lambda, [i].
rafix07,

1
Vale la pena notare che il tuo uso di emplace_backè strano: emplace_backprende un elenco di argomenti e lo passa a un costruttore per std::thread. Hai passato un'istanza (rvalore) di std::thread, quindi costruirai un thread, quindi sposterai quel thread nel vettore. Tale operazione è meglio espressa dal metodo più comune push_back. Sarebbe più sensato scrivere threads.emplace_back([i](){ print_id(i); });(costruire sul posto) o threads.push_back(std::thread([i](){ print_id(i); }));(costruire + spostare) che sono in qualche modo più idiomatici.
Milo Brandt

Risposte:


17

La [&]sintassi sta causando il'acquisizione per riferimento . Quindi abbastanza spesso quindi isarà ulteriormente avanzato quando il thread viene eseguito di quanto ci si possa aspettare. Più seriamente, il comportamento del codice è indefinito se noni rientra nell'ambito prima dell'esecuzione di un thread.

Catturare iper valore - ovvero std::thread([i](){ print_id(i); })è la correzione.


2
O meno usato e non spesso consigliabilestd::thread([=](){ print_id(i); })
Wander3r

3
Il comportamento è già indefinito perché si tratta di una corsa di dati sul (non atomico) icon la scrittura del thread principale e la lettura degli altri thread.
noce,

6

Due problemi:

  1. Non hai alcun controllo su quando viene eseguito il thread, il che significa che il valore della variabile iin lambda potrebbe non essere quello che ti aspetti.

  2. La variabile iè locale solo per il ciclo e il ciclo. Se il ciclo termina prima dell'esecuzione di uno o più thread, tali thread avranno un riferimento non valido a una variabile la cui durata è terminata.

È possibile risolvere entrambi questi problemi semplicemente catturando la variabile i per valore anziché per riferimento. Ciò significa che ogni thread avrà una copia del valore e tale copia verrà creata in modo univoco per ciascun thread.


5

Un'altra cosa:
non aspettare di avere sempre una sequenza ordinata: 0, 1, 2, 3, ... perché la modalità di esecuzione multithreading ha una specificità: indeterminismo .

Indeterminismo significa che l'esecuzione dello stesso programma, nelle stesse condizioni, dà un risultato diverso.

Ciò è dovuto al fatto che il sistema operativo pianifica i thread in modo diverso da un'esecuzione all'altra a seconda di diversi parametri: carico della CPU, priorità di altri processi, possibili interruzioni del sistema, ...

Il tuo esempio contiene solo 5 thread, quindi è semplice, prova ad aumentare il numero di thread e, ad esempio, sospendi la funzione di elaborazione, vedrai che il risultato può essere diverso da un'esecuzione all'altra.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.