2013-10-23 113 views
1

我已經通過了一些線程教程,但很好奇一件事。std ::線程何時執行線程?

std::thread Child([](){ std::cout << "When am I executed?" << std::endl << std::endl; }); 

//Some other code 

Child.join(); 

//I'm guessing now my thread would be running 

當我打電話join()還是它正在創建時的線程,當我打電話加入之間的某個時候運行正在執行的線程?如果在調用join()時執行它,只是爲了檢查我的理解,它告訴要執行的部分,並且程序在主線程上繼續,並且最終子線程在主線程正在使用的同一內存上完成了一些工作?

如果我想爲泛型類創建一個包裝,我會想要做類似下面的事情,但我似乎無法完全弄清楚。我對內存管理線程感到困惑。

class Sync { 
private: 
    int val; 

public: 
    Sync() : val(0) {} 
    void Inc() {val++} 
    int Value() { return val; } 
}; 

class Async { 
private: 
    Sync Foo; 
    std::mutex mtx; 
    std::vector<std::thread*> Children; 
public: 
    //I would need a new thread each time I called Inc 
    void Inc() { 
     Children.push_back(new std::thread([&]() { 
      mtx.lock(); 
      Foo.Inc(); 
      mtx.unlock(); 
     })); 


    } 
    //But how do I know when it is safe to delete Child? 
    int Value() { 
     for(auto& thds : Children) { 
      thds->join(); 
      delete thds; 
     } 
     Children.clear(); 
     return Foo.Value(); } 
}; 

我正在考慮一個合適的位置可能是在線程函數結束,因爲孩子將不再需要,但如果你試圖從內部摧毀自身的線程會發生什麼?我猜這聽起來會像一個壞主意一樣。我怎麼知道什麼時候可以刪除我的線程?有更好的方法嗎?

修改上面的代碼以反映來自下面的建議。

我現在已經意識到教程中關於拋出異常的問題,所以我應該使用互斥鎖而不是mtx.lock()。

回答

4

join的目的是從本質上等待線程完成。因此,一旦Child->join();返回,您的線程已完成。當然,你也可以在析構函數中執行Child->join() [或者在某個其他位置,只要確保它在某個點被調用]。

請注意,線程將在實際創建時間和連接結束之間的某個時間點開始運行。沒有辦法知道什麼時候會發生。如果系統上已有很多線程正在運行,則時間將在所有線程之間分配,並且線程可能不會運行幾秒鐘。另一方面,如果有一個CPU坐在那裏「旋轉它的手指」,它可能很可能在主線程脫離new std::thread(...)構造之前啓動[因爲std::thread將不會返回,直到線程被正確創建,並且一些數據正在存儲在線程對象中,所以線程可能在您到達Child->join()時完成。如果不知道系統處於何種狀態,則無法分辨出這些選項中的哪一個。

+0

然後線程一旦創建就開始運行?另外,我可以在加入呼叫後調用刪除。 謝謝 – Chemistpp

+2

是的,我剛剛編輯了答案,以澄清該線程可能會在創建完成之前啓動,或者稍後再完成。這完全取決於系統中有多少個CPU /內核以及系統的總體負載是多少。 –

1

您無法知道您的線程將以哪種順序或哪個內核運行。

一個posix線程基本上被linux視爲一個進程與另一個進程共享它的堆。內核將安排它們並決定使用哪個內核。例如,查看生成的(受保護的)程序集,您將看不到任何「多核/多線程編程」,因爲這是在內核級完成的。

這就是爲什麼函數如連接和諸如互斥/信號量之類的工件存在的原因。照顧比賽條件和其他相關的東西。

1

當我調用join()或正在運行 時,線程在創建線程和我何時調用join之間執行?

線程在創建線程之後執行(啓動),具體取決於可能需要一些時間的可用資源。 join()方法等待(塊),直到線程完成其工作。

一個建議:我不會將該變量的名稱命名爲Sync對象This,它令人困惑,因爲存在this關鍵字。

在調用join()Async::Inc()之後,您可以安全地刪除std::thread對象,此外,您不需要將引用存儲爲成員變量,它僅在該函數中使用。

您也可以查看<atomic>表頭,std::atomic<int>std::atomic_int

4

操作系統很重要,但它相當普遍。線程獲得預定執行。當操作系統開始實際運行時,它是完全不可預知的。在具有多核的機器上,「快速」出現的可能性很大,今天普遍可用。

thread :: join()只是確保線程完成執行。你會從來沒有實際上寫這樣的代碼,它是完全沒有意義的啓動一個線程,然後等待它完成。不妨直接執行線程的代碼。創建線程不會導致操作系統陷入困境,同樣的結果。

+0

我現在可以看到調用join的同一個函數和sync版本是一樣的。現在,我必須多考慮一點,我更瞭解這一點。謝謝 – Chemistpp