Załóżmy, że zaczynam std::thread
a detach()
, więc wątek kontynuuje wykonywanie, mimo że ten, std::thread
który kiedyś go reprezentował, wykracza poza zakres.
Załóżmy ponadto, że program nie ma niezawodnego protokołu do przyłączania się do odłączonego wątku 1 , więc odłączony wątek nadal działa po zakończeniu main()
.
Nie mogę znaleźć niczego w standardzie (a dokładniej w szkicu N3797 C ++ 14), który opisuje, co powinno się stać, ani 1.10, ani 30.3 nie zawierają stosownego sformułowania.
1 Kolejne, prawdopodobnie równoważne, pytanie brzmi: „czy odłączony wątek może kiedykolwiek zostać ponownie połączony”, ponieważ niezależnie od tego, jaki protokół zostanie wymyślony do przyłączenia, część sygnalizacyjna musiałaby zostać wykonana, gdy wątek nadal działał, a harmonogram systemu operacyjnego mógłby zdecydować o uśpieniu wątku na godzinę tuż po zakończeniu sygnalizacji, bez możliwości niezawodnego wykrycia przez odbiorcę, że wątek faktycznie się zakończył.
Jeśli wyczerpanie się main()
z odłączonymi wątkami jest niezdefiniowanym zachowaniem, to każde użycie std::thread::detach()
jest niezdefiniowane, chyba że główny wątek nigdy nie kończy pracy 2 .
Zatem wyczerpanie się main()
z odłączonymi wątkami musi mieć określone efekty. Pytanie brzmi: gdzie (w standardzie C ++ , nie POSIX, nie dokumentacja systemu operacyjnego, ...) są te efekty zdefiniowane.
2 Oderwanej nitki nie można łączyć (w sensie std::thread::join()
). Ty może czekać na wyniki z wolnostojących wątków (np poprzez przyszłość z std::packaged_task
lub przez semafor liczenia lub flagi i zmiennej stan), ale to nie gwarantuje, że wątek zakończeniu wykonywania . Rzeczywiście, chyba że można umieścić rolę sygnalizacyjną do destructor pierwszego automatycznego przedmiotu wątku, nie będzie w ogóle, być kod (destruktory), który prowadzony po kodu sygnałowego. Jeśli system operacyjny zaplanuje, że główny wątek zużyje wynik i zakończy działanie, zanim odłączony wątek zakończy działanie wspomnianych destruktorów, co ^ Wis zostanie zdefiniowane jako wystąpienie?
std::exit
lub wyjściem zmain
jest wystarczające, ale nie jest konieczne, aby spełnić te wymagania”. (cały akapit może mieć znaczenie) Zobacz także [support.start.term] / 8 (std::exit
jest wywoływany, gdymain
zwraca)Odpowiedzi:
Odpowiedź na pierwotne pytanie „co dzieje się z odłączonym wątkiem po zakończeniu działania
main()
” brzmi:Nadal działa (ponieważ standard nie mówi, że jest zatrzymany) i jest to dobrze zdefiniowane, o ile nie dotyka ani (automatic | thread_local) zmiennych innych wątków ani obiektów statycznych.
Wydaje się, że jest to dozwolone, aby pozwolić menedżerom wątków na statyczne obiekty (uwaga w [basic.start.term] / 4 mówi to samo, dzięki @dyp za wskaźnik).
Problemy pojawiają się po zakończeniu niszczenia obiektów statycznych, ponieważ wtedy wykonanie wchodzi w stan, w którym może być wykonywany tylko kod dozwolony w programach obsługi sygnału ( [basic.start.term] / 1, pierwsze zdanie ). Ze standardowej biblioteki C ++ jest to tylko
<atomic>
biblioteka ( [support.runtime] / 9, drugie zdanie ). W szczególności to - ogólnie - wykluczacondition_variable
(jest zdefiniowane w implementacji, czy jest to zapisywane do użycia w obsłudze sygnału, ponieważ nie jest częścią<atomic>
).Jeśli w tym momencie nie rozwinąłeś swojego stacka, trudno jest zrozumieć, jak uniknąć niezdefiniowanego zachowania.
Odpowiedź na drugie pytanie „czy odłączone wątki mogą być kiedykolwiek ponownie połączone” brzmi:
Tak, z
*_at_thread_exit
rodziny funkcji (notify_all_at_thread_exit()
,std::promise::set_value_at_thread_exit()
...).Jak zaznaczono w przypisie [2] pytania, sygnalizacja zmiennej warunkowej lub semafora lub licznika atomowego nie wystarczy do przyłączenia się do odłączonego wątku (w sensie zapewnienia, że koniec jego wykonania nastąpił przed odebraniem wspomniana sygnalizacja przez oczekujący wątek), ponieważ generalnie będzie więcej kodu wykonanego np. po
notify_all()
zmiennej warunkowej, w szczególności destruktory obiektów automatycznych i lokalnych dla wątków.Uruchomienie sygnalizacji jako ostatnią rzeczą wątek robi ( po destruktory automatycznych i nici lokalnego obiektów ma-stało ) jest to, co
_at_thread_exit
rodzina funkcji została zaprojektowana.Tak więc, aby uniknąć niezdefiniowanego zachowania w przypadku braku jakichkolwiek gwarancji implementacji powyżej tego, czego wymaga standard, musisz (ręcznie) połączyć odłączony wątek z
_at_thread_exit
funkcją wykonującą sygnalizację lub sprawić, aby odłączony wątek wykonywał tylko kod, który byłby bezpieczny dla obsługa sygnału też.źródło
exit
zakończeniu niszczenia statycznych obiektów, uruchomionychatexit
procedur obsługi, opróżniania strumieni itp. Zwraca sterowanie do środowiska hosta, tj. Proces kończy się. Jeśli odłączony wątek nadal działa (i w jakiś sposób uniknął niezdefiniowanego zachowania, nie dotykając niczego poza własnym wątkiem), po prostu znika w obłoku dymu, gdy proces kończy się.main
wywołaniapthread_exit
zamiast zwracać lub wywoływaćexit
, spowoduje to, że proces będzie czekał na zakończenie odłączonych wątków, a następnie wywołaexit
po zakończeniu ostatniego.Odłączanie wątków
Według
std::thread::detach
:Od
pthread_detach
:Odłączanie wątków służy głównie do oszczędzania zasobów, w przypadku gdy aplikacja nie musi czekać na zakończenie wątku (np. Demony, które muszą działać do zakończenia procesu):
std::thread
obiektowi wyjść poza zakres bez łączenia, co zwykle prowadzi do wezwania dostd::terminate()
zniszczenia.Zabijanie wątków
Zachowanie po zakończeniu procesu jest takie samo, jak w przypadku wątku głównego, który może przynajmniej przechwycić niektóre sygnały. To, czy inne wątki mogą obsługiwać sygnały, nie jest tak ważne, ponieważ można łączyć lub kończyć inne wątki w ramach wywołania funkcji obsługi sygnału głównego wątku. (Powiązane pytanie )
Jak już wspomniano, każdy wątek, odłączony lub nie, umrze wraz z procesem w większości systemów operacyjnych . Sam proces można zakończyć przez podniesienie sygnału, wywołanie
exit()
lub powrót z funkcji głównej. Jednak C ++ 11 nie może i nie próbuje zdefiniować dokładnego zachowania bazowego systemu operacyjnego, podczas gdy programiści maszyny wirtualnej Java z pewnością mogą do pewnego stopnia wyabstrahować takie różnice. AFAIK, egzotyczne modele procesów i wątków są zwykle spotykane na starożytnych platformach (do których C ++ 11 prawdopodobnie nie zostanie przeniesiony) i różnych systemach wbudowanych, które mogą mieć specjalną i / lub ograniczoną implementację biblioteki językowej, a także ograniczoną obsługę języków.Obsługa wątków
Jeśli wątki nie są obsługiwane,
std::thread::get_id()
powinny zwrócić nieprawidłowy identyfikator (domyślnie skonstruowanystd::thread::id
), ponieważ istnieje zwykły proces, który nie potrzebuje obiektu wątku do uruchomienia, a konstruktor astd::thread
powinien wyrzucić plikstd::system_error
. Oto jak rozumiem C ++ 11 w połączeniu z dzisiejszymi systemami operacyjnymi. Jeśli istnieje system operacyjny z obsługą wątków, który nie tworzy głównego wątku w swoich procesach, daj mi znać.Kontrolowanie wątków
Jeśli trzeba zachować kontrolę nad wątkiem w celu prawidłowego zamknięcia, można to zrobić za pomocą prymitywów synchronizacji i / lub jakiegoś rodzaju flag. Jednak w tym przypadku ustawienie flagi zamknięcia, po której następuje łączenie, jest sposobem, który preferuję, ponieważ nie ma sensu zwiększać złożoności przez odłączanie wątków, ponieważ i tak zasoby zostałyby zwolnione w tym samym czasie, gdzie kilka bajtów
std::thread
obiektu w porównaniu z wyższą złożonością i prawdopodobnie bardziej prymitywami synchronizacji powinny być dopuszczalne.źródło
Rozważ następujący kod:
Uruchamiając go w systemie Linux, wiadomość z thread_fn nigdy nie jest drukowana. System operacyjny rzeczywiście czyści się
thread_fn()
zaraz pomain()
zamknięciu. Wymianat1.detach()
zt1.join()
zawsze drukuje wiadomość, jak oczekiwano.źródło
Los wątku po zakończeniu programu jest niezdefiniowanym zachowaniem. Ale nowoczesny system operacyjny czyści wszystkie wątki utworzone przez proces po jego zamknięciu.
Podczas odłączania
std::thread
te trzy warunki będą nadal obowiązywać:*this
nie posiada już żadnego wątkujoinable()
zawsze będzie równafalse
get_id()
będzie równastd::thread::id()
źródło
detach()
nieokreślonego zachowania? Trudno w to uwierzyć ...Kiedy wątek główny (czyli wątek, który uruchamia funkcję main ()) zostaje zakończony, proces zostaje zakończony, a wszystkie inne wątki zatrzymują się.
Źródła: https://stackoverflow.com/a/4667273/2194843
źródło
Aby umożliwić innym wątkom kontynuowanie wykonywania, główny wątek powinien zakończyć się przez wywołanie pthread_exit () zamiast exit (3). Dobrze jest użyć pthread_exit w main. Kiedy używany jest pthread_exit, główny wątek przestanie działać i pozostanie w stanie zombie (niedziałający), dopóki wszystkie inne wątki nie zostaną zakończone. Jeśli używasz pthread_exit w głównym wątku, nie możesz uzyskać statusu zwrotu innych wątków i nie możesz wyczyścić innych wątków (można to zrobić za pomocą pthread_join (3)). Ponadto lepiej jest odłączać wątki (pthread_detach (3)), aby zasoby wątków były automatycznie zwalniane po zakończeniu wątku. Udostępnione zasoby nie zostaną zwolnione, dopóki wszystkie wątki nie zostaną zakończone.
źródło