2009-11-15 45 views
8

我如何可以等待分離線程在C++來完成?等待分離的線程來完成在C++

我不關心的退出狀態,我只是想知道線程是否已經完成。

我試圖提供圍繞異步thirdarty工具同步封裝。問題是一個涉及回調的奇怪的競賽狀況崩潰。的進程是:

  1. 我所說的第三方,並註冊一個回調
  2. 當第三方完成,它使用回調通知我 - 在一個分離的線程我沒有真正的控制權。
  3. 我想從(1)要等到(2)被稱爲線程。

我想提供一個阻塞調用的機制來包裝這個。到目前爲止,我有:

class Wait { 
    public: 
    void callback() { 
    pthread_mutex_lock(&m_mutex); 
    m_done = true; 
    pthread_cond_broadcast(&m_cond); 
    pthread_mutex_unlock(&m_mutex); 
    } 

    void wait() { 
    pthread_mutex_lock(&m_mutex); 
    while (!m_done) { 
     pthread_cond_wait(&m_cond, &m_mutex); 
    } 
    pthread_mutex_unlock(&m_mutex); 
    } 

    private: 
    pthread_mutex_t m_mutex; 
    pthread_cond_t m_cond; 
    bool   m_done; 
}; 

// elsewhere... 
Wait waiter; 
thirdparty_utility(&waiter); 
waiter.wait(); 

據我所知,這應該工作,它通常會,但有時它會崩潰。據我可以從核心文件確定,我猜測,這個問題是這樣的:

  1. 當回調廣播m_done結束,等待線程醒來
  2. 等待線程現在都是在這裏完成,等待被破壞。 Wait的所有成員都被銷燬,包括互斥和cond。
  3. 回調線程試圖從廣播點繼續,但現在使用的內存的被釋放,從而導致內存損壞。
  4. 當回調線程試圖返回時(高於我可憐的回調方法的級別),程序崩潰(通常使用SIGSEGV,但我曾多次看到SIGILL)。

我試了很多不同的機制來嘗試解決這個問題,但是他們都沒有解決這個問題。我仍然偶爾看到碰撞。

編輯:更多詳細信息:

這是一個大規模多線程應用程序的一部分,所以創建靜態等待是不切實際的。

我跑了一個測試,在堆上創建Wait,並故意泄漏內存(即Wait對象從不釋放),導致沒有崩潰。所以我相信這是一個等待被釋放太快的問題。

我也試過在wait解鎖後用sleep(5)進行測試,也沒有產生崩潰。儘管如此,我討厭依賴於這樣的混亂。

編輯:第三方細節:

我不認爲這是在第一個相關的,但我認爲它的越多,我認爲這是真正的問題:

的第三方的東西我提到過,爲什麼我無法控制線程:這是使用CORBA。

因此,CORBA有可能比我預期的更長時間地堅持對我的對象的引用。

回答

3

是的,我相信你正在描述的是正在發生的事情(取消分配的競爭條件)。解決這個問題的一個快速方法是創建一個靜態的Wait實例,一個不會被破壞的實例。只要你不需要同時有一個以上的服務員,這將會起作用。

您也將永久使用該內存,它不會釋放。但它看起來不那麼糟糕。

主要問題是很難在線程之間協調線程通信結構的生命週期:至少在沒有垃圾回收的語言中,至少需要一個剩餘的通信構造來進行通信C++)。

編輯: 請參閱有關使用全局互斥對refcounting的一些想法的註釋。

+0

不幸的是,這是一個大規模的多線程應用程序,我們真的想爲每個應用程序分別使用不同的Wait對象 - 否則它會讓我們太慢。 – Tim 2009-11-15 03:18:45

+0

另外,如果我們使用一個靜態的Wait,那麼就有一個問題來試圖協調哪個線程需要恢復。 – Tim 2009-11-15 03:28:13

+0

好的,你可以做到這一點。您可以將一個refcount字段添加到由全局互斥鎖保護的Wait對象。開始2的refcount,然後有回調和服務員都完成後遞減refcount。如果全局互斥變成瓶頸,還有其他更復雜的解決方案。 – 2009-11-15 03:29:25

0

據我所知,直接詢問線程是否完成運行(即沒有0​​函數),就沒有可移植的方法。你在做什麼正確的方式來做到這一點,至少在你有信號的情況下。如果你看到崩潰,你肯定是由於Wait對象正在被釋放當創建它的線程退出(而不是一些其他微妙的鎖定問題 - 這很常見),問題是,你需要確保Wait不是被解除分配,通過管理一個不同於通知的線程。將其放入全局內存或動態分配它並與該線程共享。最簡單的就是沒有爲自己的內存等待等待,有線程正在等待擁有它。

0

您是否正確初始化並銷燬互斥鎖​​和條件變量?

Wait::Wait() 
{ 
    pthread_mutex_init(&m_mutex, NULL); 
    pthread_cond_init(&m_cond, NULL); 
    m_done = false; 
} 

Wait::~Wait() 
{ 
    assert(m_done); 
    pthread_mutex_destroy(&m_mutex); 
    pthread_cond_destroy(&m_cond); 
} 

確保您不會過早破壞Wait對象 - 如果它在一個線程中被摧毀,而另一個線程仍然需要它,你會得到一個競爭條件,這將有可能導致段錯誤。我建議使它成爲一個全局靜態變量,它在程序初始化時(在main()之前)構造並在程序退出時被破壞。

+0

是的,互斥和cond被正確初始化/銷燬。我實際上使用了經過良好測試的包裝類。是的,我確定Wait被過早銷燬 - 而一個線程仍然在Wait :: callback中。 – Tim 2009-11-15 03:30:51

0

如果你的假設是正確的,那麼第三方模塊看起來是越野車,你需要想出一些破解來讓你的應用程序工作。

Static Wait是不可行的。如何Wait池(它甚至可能按需增長)?你使用線程池運行應用程序嗎? 儘管在第三方模塊仍在使用時仍會重複使用相同的Wait。但是,您可以通過適當排列池中空置的等待來最大限度地減少此類機會。

聲明:我絕不是線程安全方面的專家,因此請將此文作爲外行的建議。