Próbuję zrozumieć wielowątkowość w c ++, ale utknąłem w tym problemie: jeśli uruchomię wątki w pętli for, wypiszą one nieprawidłowe wartości. To jest kod:
#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;
}
Spodziewałem się wydrukowania wartości 0,1,2,3,4, ale często otrzymywałem tę samą wartość dwukrotnie. To jest wynik:
Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5
Czego mi brakuje?
c++
multithreading
Ermando
źródło
źródło
i
przez wartość lambda,[i]
.emplace_back
jest dziwne:emplace_back
bierze listę argumentów i przekazuje ją konstruktorowistd::thread
. Minąłeś instancję (rvalue)std::thread
, dlatego zbudujesz wątek, a następnie przeniesiesz ten wątek do wektora. Operację tę lepiej wyraża bardziej popularna metodapush_back
. Bardziej sensowne byłoby pisaniethreads.emplace_back([i](){ print_id(i); });
(konstruowanie na miejscu) lubthreads.push_back(std::thread([i](){ print_id(i); }));
(konstruowanie + ruch), które są nieco bardziej idiomatyczne.Odpowiedzi:
[&]
Składni powodujei
być ujęte przez odniesienie . Dlatego dość częstoi
będą dalej zaawansowane, gdy wątek działa, niż można się spodziewać. Mówiąc poważniej, zachowanie kodu jest niezdefiniowane, jeślii
wykracza poza zakres przed uruchomieniem wątku.Przechwytywanie
i
według wartości - tostd::thread([i](){ print_id(i); })
jest poprawka.źródło
std::thread([=](){ print_id(i); })
i
z zapisywaniem głównego wątku i odczytywaniem pozostałych wątków.Dwa problemy:
Nie masz kontroli nad uruchomieniem wątku, co oznacza, że wartość zmiennej
i
w lambda może nie być zgodna z oczekiwaniami.Zmienna
i
jest lokalna tylko dla pętli i dla pętli. Jeśli pętla zakończy się przed uruchomieniem jednego lub więcej wątków, wątki te będą miały niepoprawne odwołanie do zmiennej, której okres ważności dobiegł końca.Możesz rozwiązać oba te problemy w bardzo prosty sposób, przechwytując zmienną
i
według wartości zamiast referencji. Oznacza to, że każdy wątek będzie miał kopię wartości, a ta kopia zostanie utworzona indywidualnie dla każdego wątku.źródło
Kolejna rzecz:
nie czekaj, aż będziesz mieć zawsze uporządkowaną sekwencję: 0, 1, 2, 3, ... ponieważ tryb wykonywania wielowątkowości ma swoją specyfikę: nieokreśloność .
Indeterminizm oznacza, że wykonanie tego samego programu w tych samych warunkach daje inny wynik.
Wynika to z faktu, że system operacyjny planuje wątki inaczej w zależności od wykonania, w zależności od kilku parametrów: obciążenie procesora, priorytet innych procesów, możliwe przerwy w systemie, ...
Twój przykład zawiera tylko 5 wątków, więc jest to proste, spróbuj zwiększyć liczbę wątków, a na przykład przespać funkcję przetwarzania, zobaczysz, że wynik może być różny w zależności od wykonania.
źródło