2017-02-22 151 views
0

我正在寫一個服務器,允許在同一時間從多個客戶端連接。我爲每個新連接創建一個新線程。客戶端通過此線程與服務器進行通信。當客戶端與服務器斷開連接時,我需要將該線程的內存重新分配到服務器進程中。C++線程在其末尾刪除

問題是當我嘗試「刪除線程本身」。

我寫了一些代碼來解釋我的問題。 C類代表一個新的連接。我爲每個連接動態地創建這個類的新對象。該功能行爲被自願地簡化以解釋問題。

#include <thread> 
#include <iostream> 
#include <mutex> 
#include <chrono> 

using namespace std; 

mutex m; 

class C { 
    thread *t; 

    static void action(int n, C* obj) { 
    for (int i = 0; i < 10; i++) { 
     m.lock(); 
     cout << "i:" << i << ", n:" << n << endl; 
     m.unlock(); 
    } 
    delete(obj); 
    } 

    public: 
    C() : t(nullptr) {} 

    ~C() { 
     delete(t); // Work if I comment this line 
    } 

    void launch() { 
     static int i = 0; 
     t = new thread(action, i++, this); 
    } 

}; 

int main() { 
    C *c1 = new C(); 
    c1->launch(); 

    this_thread::sleep_for(chrono::seconds(5)); 
    return 0; 
} 

對不起,我正在學習它。

+0

爲什麼你甚至使用指針?您可以使用不需要清理的自動對象執行此操作。 – NathanOliver

+0

@LightnessRacesinOrbit這是* C++而不是C#,嘆氣。橄欖球:在演示的代碼中不需要「新」或指針。避免使用名稱空間標準符號,標準符號中包含太多符號。 – Yakk

+0

考慮使用線程_pool_,而不是爲每個新連接創建一個新線程。我不知道C++是否有足夠的線程池實現可以使用,但這是一個簡單的想法:您有一個阻塞隊列_tasks_(在這種情況下,每個任務都是一個代表客戶端的對象,它正在等待),並且你有一些線程永遠循環嘗試從隊列中取出任務。當一個線程得到一個任務時,它執行任務(即,它服務於客戶端),然後不再死亡,而是回到等待下一個任務。 –

回答

2

在銷燬std::thread之前,您必須與t->join()「加入」。

參考this documentation

如果*this有一個關聯的線程(joinable() == true),std::terminate()被調用。

儘管如此,更致命的是,您正試圖從線程內部完成所有這些工作。你可以這樣做:

例外如果發生錯誤,
std::system_error

錯誤條件
resource_deadlock_would_occur如果this->get_id() == std::this_thread::get_id()(死鎖檢測)

順便說一句,你應該失去的動態分配。你不需要它。

+0

如果我在刪除它之前嘗試加入線程,程序會拋出一個異常「std :: system_error」 – olive007

+0

@ olive007:仔細看看「action」。看起來你正試圖從自己刪除線程...。爲什麼?您遇到'resource_deadlock_would_occur'錯誤情況。我認爲你應該重新審視你的設計! –

+0

年我試圖做到這一點。我從自己刪除線程,因爲如果我不這樣做,內存泄漏,我的服務器使用太多的內存。 – olive007

1

調用joinable線程上的析構函數被指定爲程序的終止。它必須是joineddetached。利用每個連接一個線程是不是最好的:

此外,如果出現異常如您將無法正常釋放鎖等

作爲一個側面說明,你應該使用那些RAII助手,並lock_guards互斥資源的使用,除非您希望有少量的同時連接。