[英]How does joining threads affect the order of execution in the main thread?
我知道線程並發運行所以你無法預測執行的順序,但在提供的代碼中,我在運行其他任何東西之前加入了線程t4
。 如果.join()
應該等到線程執行完畢,那么為什么訂單仍然是隨機的? 在兩個打印語句之前加入任何內容總是會導致它們最后打印,而如果我之后加入所有內容,它並不總是最后一個,為什么?
void task() {
std::cout << "task 1 says Hi\n";
}
void task2() {
std::cout << "task 2 says Hi\n";
}
void task3() {
std::cout << "task 3 says Hi\n";
}
void task4() {
std::cout << "task 4 says Hi\n";
}
int main() {
std::thread t1(task);
std::thread t2(task2);
std::thread t3(task3);
std::thread t4(task4);
t4.join();
std::cout << "main says Hi 1\n";
// synchronize - IMPORTANT!
t2.join();
t3.join();
t1.join();
std::cout << "main says Hi 2" << std::endl;
system("pause");
}
std::thread::join
僅阻止當前線程,直到*this
標識的線程完成其執行。
所以這向你保證的是, main says Hi 1
不會在task 4 says Hi
之前打印,並且main says Hi 2
在其他三個任務完成執行之前沒有打印。
四個任務中的printf
語句可以按任何順序打印,並且來自不同線程的輸出也可能交錯。
當您在創建時啟動線程時,線程很可能在您加入它們之前執行它們的代碼。 首先創建t1
, t1
最可能先執行,然后在另一個中創建它們。
join
你的t4
並不能保證它在t1
或你創建的任何其他線程之前執行。 它只是等待t4
的終止。
Visual Studio中的快速測試顯示:
task 4 says Hi
task 2 says Hi
task 3 says Hi
task 2 says Hi
main says Hi 1
main says Hi 2
使用以下代碼(代碼段):
// ...
std::thread t4(task2);
std::thread t2(task2);
std::thread t3(task3);
std::thread t2(task2);
// ...
連接線程的終止與連接線程中join()
的結束同步 。 簡單地說,這意味着后會發生在連接螺紋一切join()
也有保證,在加入線程發生的一切之后發生。 C ++標准中有一些地方與這種關系建立強同步(例如,一個線程中的原子存儲與內存順序std::memory_order_seq_cst
同步 - 如果加載的值是一個, 則與另一個線程上的原子加載同步存儲在第一個線程中)。 還有一些較弱的保證,例如對於內存訂單較弱的原子訪問。
您可以將生成的執行視為執行由這些同步關系產生的有向非循環圖的拓撲排序的可能結果之一。 (如果圖形是循環的,那么就會出現死鎖。)
具體而言,如果從它們中的任何一個到另一個不存在同步路徑,則不保證操作的相對順序。 你不能向后遍歷邊緣,但這似乎是你期望事情發展的方式。 僅僅因為主線程的線程1日前加入線程4,這並不意味着在線程4 什么的之前發生的線程1. 什么對於這個是成立的,你就需要引入一個額外的邊緣到DAG或重新排序操作使這成為真(即可證明)。 例如,你可以從主線程開始線程1,只有在線程4的連接之后 - 然后 ,線程1中發生的任何事情都會發生在線程4中發生的所有事情之后,因為線程創建與新線程的開頭同步,然后建立一個如下所示的傳遞依賴:
thread4.join()
同步。 thread1
之前(同樣,僅因為它是同一個線程)。 thread1
上發生的所有操作同步。 TL; DR:線程同步原則上是單向的。 它就像刷新緩沖區(事實上,它可能涉及在硬件級別上刷新所謂的存儲緩沖區):它保證所有先前的寫入都已完成,但它不會延遲任何形式的任何先前寫入。 如果需要更嚴格的保證,則必須使用不同的同步方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.