Estou tentando entender o Multi-threading em c ++, mas estou preso neste problema: se eu lançar threads em um loop for, eles imprimirão valores incorretos. Este é o 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;
}
Eu esperava imprimir os valores 0,1,2,3,4, mas muitas vezes obtive o mesmo valor duas vezes. Esta é a saída:
Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5
O que estou perdendo?
c++
multithreading
Ermando
fonte
fonte
i
por valor para lambda[i]
,.emplace_back
é estranho:emplace_back
pega uma lista de argumentos e a passa para um construtor destd::thread
. Você passou por uma instância (rvalue) destd::thread
, portanto, construirá um thread e depois moverá o thread para o vetor. Essa operação é melhor expressa pelo método mais comumpush_back
. Seria mais sensato escreverthreads.emplace_back([i](){ print_id(i); });
(construir no lugar) outhreads.push_back(std::thread([i](){ print_id(i); }));
(construir + mover) que são um pouco mais idiomáticos.Respostas:
A
[&]
sintaxe está causandoi
a captura por referência . Por isso, muitas vezesi
será mais avançado quando o thread for executado do que você poderia esperar. Mais a sério, o comportamento do seu código é indefinido sei
ficar fora do escopo antes da execução de um encadeamento.Capturar
i
por valor - ou seja,std::thread([i](){ print_id(i); })
é a correção.fonte
std::thread([=](){ print_id(i); })
i
com a gravação do thread principal e os outros threads lendo.Dois problemas:
Você não tem controle sobre quando o encadeamento é executado, o que significa que o valor da variável
i
no lambda pode não ser o que você espera.A variável
i
é local apenas para o loop e o loop. Se o loop terminar antes que um ou mais encadeamentos sejam executados, esses encadeamentos terão uma referência inválida a uma variável cuja vida útil terminou.Você pode resolver esses dois problemas de maneira muito simples capturando a variável
i
por valor, em vez de por referência. Isso significa que cada segmento terá uma cópia do valor e essa cópia será feita exclusivamente para cada segmento.fonte
Outra coisa:
não espere até ter sempre uma sequência ordenada: 0, 1, 2, 3, ... porque o modo de execução multithreading tem uma especificidade: indeterminismo .
Indeterminismo significa que a execução do mesmo programa, nas mesmas condições, produz um resultado diferente.
Isso ocorre porque o sistema operacional agenda os threads de maneira diferente de uma execução para outra, dependendo de vários parâmetros: carga da CPU, prioridade de outros processos, possíveis interrupções do sistema, ...
Seu exemplo contém apenas 5 threads, portanto, é simples, tente aumentar o número de threads e, por exemplo, adormeça na função de processamento, você verá que o resultado pode ser diferente de uma execução para outra.
fonte