Estoy tratando de entender Multi-threading en c ++, pero estoy atrapado en este problema: si inicio hilos en un bucle for, imprimen valores incorrectos. Este es el código:
#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;
}
Esperaba que me imprimieran los valores 0,1,2,3,4 pero a menudo obtuve el mismo valor dos veces. Esta es la salida:
Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5
¿Qué me estoy perdiendo?
c++
multithreading
Ermando
fuente
fuente
i
por el valor de lambda,[i]
.emplace_back
es extraño:emplace_back
toma una lista de argumentos y la pasa a un constructor parastd::thread
. Ha pasado una instancia (rvalue) destd::thread
, por lo tanto, construirá un hilo y luego lo moverá al vector. Esa operación se expresa mejor por el método más comúnpush_back
. Sería más sensato escribirthreads.emplace_back([i](){ print_id(i); });
(construir en el lugar) othreads.push_back(std::thread([i](){ print_id(i); }));
(construir + mover), que son algo más idiomáticos.Respuestas:
La
[&]
sintaxis está haciendoi
que se capture por referencia . Por lo tanto, con bastante frecuenciai
, se avanzará más cuando se ejecute el hilo de lo que cabría esperar. Más en serio, el comportamiento de su código no está definido sii
sale del alcance antes de que se ejecute un subproceso.Capturar
i
por valor,std::thread([i](){ print_id(i); })
es decir, es la solución.fuente
std::thread([=](){ print_id(i); })
i
con la escritura del hilo principal y la lectura de los otros hilos.Dos problemas:
No tiene control sobre cuándo se ejecuta el subproceso, lo que significa que el valor de la variable
i
en el lambda podría no ser el esperado.La variable
i
es local para el ciclo y solo para el ciclo. Si el ciclo termina antes de que se ejecute uno o más subprocesos, esos subprocesos tendrán una referencia no válida a una variable cuya vida útil haya finalizado.Puede resolver ambos problemas de manera muy simple capturando la variable
i
por valor en lugar de por referencia. Eso significa que cada hilo tendrá una copia del valor, y esa copia se realizará de forma única para cada hilo.fuente
Otra cosa:
no espere hasta tener siempre una secuencia ordenada: 0, 1, 2, 3, ... porque el modo de ejecución de subprocesos múltiples tiene una especificidad: indeterminismo .
El indeterminismo significa que la ejecución del mismo programa, en las mismas condiciones, da un resultado diferente.
Esto se debe al hecho de que el sistema operativo programa los subprocesos de manera diferente de una ejecución a otra dependiendo de varios parámetros: carga de la CPU, prioridad de otros procesos, posibles interrupciones del sistema, ...
Su ejemplo contiene solo 5 subprocesos, por lo que es simple, intente aumentar el número de subprocesos y, por ejemplo, suspenda la función de procesamiento, verá que el resultado puede ser diferente de una ejecución a otra.
fuente