問(wèn)題描述
假設(shè)我正在啟動(dòng)一個(gè) std::thread
然后 detach()
它,所以即使 std::thread
曾經(jīng)代表它,超出了范圍.
Assume I'm starting a std::thread
and then detach()
it, so the thread continues executing even though the std::thread
that once represented it, goes out of scope.
進(jìn)一步假設(shè)程序沒有可靠的加入分離線程的協(xié)議1,所以當(dāng)main()
退出時(shí)分離線程仍然運(yùn)行.
Assume further that the program does not have a reliable protocol for joining the detached thread1, so the detached thread still runs when main()
exits.
我在標(biāo)準(zhǔn)中找不到任何內(nèi)容(更準(zhǔn)確地說(shuō),在 N3797 C++14 草案中),它描述了應(yīng)該發(fā)生的情況,1.10 和 30.3 都沒有包含相關(guān)的措辭.
I cannot find anything in the standard (more precisely, in the N3797 C++14 draft), which describes what should happen, neither 1.10 nor 30.3 contain pertinent wording.
1 另一個(gè)可能等效的問(wèn)題是:分離的線程是否可以再次加入",因?yàn)闊o(wú)論您發(fā)明要加入的協(xié)議,信令部分都必須在線程仍在運(yùn)行,操作系統(tǒng)調(diào)度程序可能會(huì)在信號(hào)剛執(zhí)行完后決定讓線程休眠一個(gè)小時(shí),而接收端無(wú)法可靠地檢測(cè)到線程實(shí)際上已完成.
1 Another, probably equivalent, question is: "can a detached thread ever be joined again", because whatever protocol you're inventing to join, the signalling part would have to be done while the thread was still running, and the OS scheduler might decide to put the thread to sleep for an hour just after signalling was performed with no way for the receiving end to reliably detect that the thread actually finished.
如果在分離線程運(yùn)行時(shí)用完 main()
是未定義的行為,那么 any 使用 std::thread::detach()
是未定義的行為,除非主線程永遠(yuǎn)不會(huì)退出2.
If running out of main()
with detached threads running is undefined behaviour, then any use of std::thread::detach()
is undefined behaviour unless the main thread never exits2.
因此,在運(yùn)行分離線程的情況下用完 main()
必須具有定義效果.問(wèn)題是:哪里(在 C++ 標(biāo)準(zhǔn)中,不是 POSIX,不是 OS 文檔,...)是那些定義的效果.
Thus, running out of main()
with detached threads running must have defined effects. The question is: where (in the C++ standard, not POSIX, not OS docs, ...) are those effects defined.
2 一個(gè)分離的線程不能被加入(在std::thread::join()
的意義上).您可以等待來(lái)自分離線程的結(jié)果(例如通過(guò)來(lái)自std::packaged_task
的未來(lái),或者通過(guò)計(jì)數(shù)信號(hào)量或標(biāo)志和條件變量),但這不會(huì)不保證線程已經(jīng)完成執(zhí)行.實(shí)際上,除非您將信號(hào)部分放入線程的第一個(gè)自動(dòng)對(duì)象的析構(gòu)函數(shù)中,否則通常將是在信號(hào)之后運(yùn)行的代碼(析構(gòu)函數(shù))代碼.如果操作系統(tǒng)安排主線程在分離的線程運(yùn)行完上述析構(gòu)函數(shù)之前使用結(jié)果并退出,那么 ^Wi 定義會(huì)發(fā)生什么?
2 A detached thread cannot be joined (in the sense of std::thread::join()
). You can wait for results from detached threads (e.g. via a future from std::packaged_task
, or by a counting semaphore or a flag and a condition variable), but that doesn't guarantee that the thread has finished executing. Indeed, unless you put the signalling part into the destructor of the first automatic object of the thread, there will, in general, be code (destructors) that run after the signalling code. If the OS schedules the main thread to consume the result and exit before the detached thread finishes running said destructors, what will^Wis defined to happen?
推薦答案
原問(wèn)題main()
退出時(shí)分離的線程會(huì)發(fā)生什么"的答案是:
The answer to the original question "what happens to a detached thread when main()
exits" is:
它會(huì)繼續(xù)運(yùn)行(因?yàn)闃?biāo)準(zhǔn)沒有說(shuō)它已停止),而且這是明確定義的,只要它既不涉及其他線程的 (automatic|thread_local) 變量也不涉及靜態(tài)對(duì)象.
It continues running (because the standard doesn't say it is stopped), and that's well-defined, as long as it touches neither (automatic|thread_local) variables of other threads nor static objects.
這似乎允許線程管理器作為靜態(tài)對(duì)象([basic.start.term]/4 中的注釋說(shuō)明了這一點(diǎn),感謝@dyp 提供了指針).
This appears to be allowed to allow thread managers as static objects (note in [basic.start.term]/4 says as much, thanks to @dyp for the pointer).
當(dāng)靜態(tài)對(duì)象的銷毀完成時(shí)會(huì)出現(xiàn)問(wèn)題,因?yàn)槿缓髨?zhí)行進(jìn)入一個(gè)只能執(zhí)行信號(hào)處理程序中允許的代碼的狀態(tài)([basic.start.term]/1, 1st sentence).在 C++ 標(biāo)準(zhǔn)庫(kù)中,只有 庫(kù)([support.runtime]/9, 2nd sentence).特別是,一般來(lái)說(shuō),排除
condition_variable
(它是實(shí)現(xiàn)定義的,是否保存以在信號(hào)處理程序中使用,因?yàn)樗皇?<原子>
).
Problems arise when the destruction of static objects has finished, because then execution enters a regime where only code allowed in signal handlers may execute ([basic.start.term]/1, 1st sentence). Of the C++ standard library, that is only the <atomic>
library ([support.runtime]/9, 2nd sentence). In particular, that—in general—excludes condition_variable
(it's implementation-defined whether that is save to use in a signal handler, because it's not part of <atomic>
).
除非此時(shí)您已經(jīng)解開堆棧,否則很難看出如何避免未定義的行為.
Unless you've unwound your stack at this point, it's hard to see how to avoid undefined behaviour.
第二個(gè)問(wèn)題分離的線程是否可以再次加入"的答案是:
The answer to the second question "can detached threads ever be joined again" is:
是的,使用 *_at_thread_exit
系列函數(shù)(notify_all_at_thread_exit()
、std::promise::set_value_at_thread_exit()
、...).
Yes, with the *_at_thread_exit
family of functions (notify_all_at_thread_exit()
, std::promise::set_value_at_thread_exit()
, ...).
正如問(wèn)題的腳注 [2] 中所指出的,向條件變量或信號(hào)量或原子計(jì)數(shù)器發(fā)出信號(hào)不足以加入分離的線程(在確保其執(zhí)行結(jié)束的意義上有-發(fā)生在等待線程接收到所述信號(hào)之前),因?yàn)橐话銇?lái)說(shuō),在例如之后會(huì)執(zhí)行更多的代碼條件變量的notify_all()
,特別是自動(dòng)和線程局部對(duì)象的析構(gòu)函數(shù).
As noted in footnote [2] of the question, signalling a condition variable or a semaphore or an atomic counter is not sufficient to join a detached thread (in the sense of ensuring that the end of its execution has-happened-before the receiving of said signalling by a waiting thread), because, in general, there will be more code executed after e.g. a notify_all()
of a condition variable, in particular the destructors of automatic and thread-local objects.
在線程做的最后一件事(在自動(dòng)和線程局部對(duì)象的析構(gòu)函數(shù)已經(jīng)發(fā)生之后)運(yùn)行信號(hào)是_at_thread_exitcode> 函數(shù)系列的設(shè)計(jì)目標(biāo).
Running the signalling as the last thing the thread does (after destructors of automatic and thread-local objects has-happened) is what the _at_thread_exit
family of functions was designed for.
因此,為了避免在沒有超出標(biāo)準(zhǔn)要求的任何實(shí)現(xiàn)保證的情況下出現(xiàn)未定義的行為,您需要(手動(dòng))將分離的線程與 _at_thread_exit
函數(shù)一起執(zhí)行信號(hào)或使分離的線程只執(zhí)行對(duì)信號(hào)處理程序也是安全的代碼.
So, in order to avoid undefined behaviour in the absence of any implementation guarantees above what the standard requires, you need to (manually) join a detached thread with an _at_thread_exit
function doing the signalling or make the detached thread execute only code that would be safe for a signal handler, too.
這篇關(guān)于當(dāng) main() 退出時(shí),分離的線程會(huì)發(fā)生什么?的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,也希望大家多多支持html5模板網(wǎng)!