2012-05-31 68 views
4

我在Linux上做串行IO在C++中。目前我正在使用阻止閱讀。這使我無法在阻塞read()中停止線程cought,除非強制終止或中斷線程或使用類似pthread取消的東西。現在遍佈網絡,我看到人們尖叫着建議他們需要終止來自阻塞IO的線程。通常它涉及的內存泄漏。是否有一些魔術內存泄漏可以在線程中斷時出現,只要您正確清理?終止使用多線程阻塞IO在Linux中C++

try 
{ 
    while(true) 
    { 
     blocking_read(fd,buffer,512); 
    } 
}catch(interrupt_exception) 
{ 

} 
//clean up, close fd, release heap memory, usual stuff 

或者是我唯一的選擇類似下面或實現更高水平的協議,確保阻塞讀取接收的標誌開關量輸入,使其能夠自行關閉。

try 
{ 
    while(running) 
    { 
     nonblocking_read(fd,buffer,512); 

     if(cancel) 
      running = false; //break return etc 
    } 
} 

//clean up, close fd, release heap memory, usual stuff 

因此,再次,如果您中斷線程導致它拋出異常,那麼在read()中是否會發生一些魔術內存泄漏?

或者我應該根本不關心,讓析構函數殺死線程(我假設當你刪除對象持有的線程線程終止)?並在那裏清理?像

class MyClass{ 
    int fd;  
    Thread* myThread; 
    ~MyClass(){ 
     delete myThread; 
     close(fd); 
    } 
}; 

感謝您的幫助!

回答

6

read()不應泄漏內存。與阻塞和非阻塞讀取,應用程序代碼仍然負責爲buf參數所提供的存儲器的管理。通過信號中斷read()不會拋出異常,因此,如果使用的信號,你將需要檢查的結果和錯誤號

  • 如果read()由信號讀取數據之前被中斷,-1將與錯誤號設置爲EINTR返回。
  • 如果read()由信號讀取一些數據後中斷,POSIX允許與錯誤號集中返回到EINTR,或用於read()返回已經讀取的字節的數量或者-1。

如果使用pthread_cancel()則會拋出異常。通過此方法,您可以選擇以下選項:

  • 通過註冊清理功能執行清理,清理功能爲pthread_cleanup_push()
  • 分配動態存儲器,經由pthread_setspecific()存儲到線程特定的存儲。
  • 通過auto_ptr/unique_ptr管理內存。
  • 趕上abi::__forced_unwind異常,執行清理並重新拋出。

一般來說,考慮避免線程取消。在可能的情況下,擁有用於擺脫循環的共享標誌更好,更易於管理。這允許線程執行任何必要的清理,並防止您的實現依賴於線程庫的實現及其任何怪癖。

對於您的情況,如果您使用阻塞式讀取,請考慮輪詢fd以查看數據是否可通過select()超時,並且只有在fd有數據時才調用read()。這使您可以定期檢查線程標誌是否設置爲不再運行,並且可以防止您需要處理信號以從read()中斷線程,因爲read()不應再阻止等待數據。

此外,刪除線程對象時發生的行爲依賴於線程庫。例如,刪除pthread_tboost::thread對關聯線程的執行沒有影響。

+0

非常實用且有用的答案。我已經在我曾工作的一個路由器相關項目中看到了這個答案的應用。感謝您提供了很多選擇 – achoora

0

pthread_cancel可以將不會直接導致魔術內存泄漏。如果您的線程希望在明確定義的取消點處停止(請參閱pthread_cancel手冊頁以瞭解更多詳細信息),那麼可以根據這一點進行設計。

以下測試程序顯示valgrind沒有錯誤,但請注意,glibc實現取消線程時(見Cancellation and C++ Exceptions),依賴於生成的異常是不可移植的。

-nick

#include <cstdio> 
#include <pthread.h> 
#include <unistd.h> 

void* thread_func(void*) 
{ 
    try 
    { 
     char buf[2048]; 
     read(1, buf, 2048); 
    } 
    catch(...) 
    { 
     fprintf(stderr, "thread %lu cancelled\n", pthread_self()); 
     throw; 
    } 
    return NULL; 
} 

int main() 
{ 
    pthread_t thread; 
    int res = pthread_create(&thread, NULL, thread_func, NULL); 
    sleep(1); 
    pthread_cancel(thread); 
    void* tres; 
    pthread_join(thread, &tres); 
    return 0; 
} 
+0

我應該補充說,非阻塞IO幾乎總是更好,但是pthread_cancel的簡單性在某些情況下使它適用。 – mythagel

0

我真的只是發送一個信號給那個線程,那就是信號的用途。在您的處理程序中將您的全局running設置爲false,並在您的第二個片段中有一個while(running)循環,就是這樣。

read不會泄漏,它讀入您已經分配的緩衝區,並且您知道。它成功或失敗(或阻止,但信號將解除阻止)。如果成功,請查看可以對數據進行的操作(它可能仍然是部分讀取或損壞或其他),否則重複,直到runningfalse

然後清理,免費分配你的東西,並優雅地退出線程(...或做任何你想做的事情)。

如果我沒有弄錯,在沒有提供SA_RESTART的情況下,安裝處理程序時根本不會出現重啓系統調用時相當複雜的「接收某些數據之前和之後」的情況。
但即使如此,如果確實出現,誰在乎 - 最終,系統調用只能是失敗或成功,並且即使在沒有信號的情況下也可以返回部分數據,因此您必須始終爲無論如何。因此,你真的只關心你是否收集了足夠的數據來做一些有用的事情,或者由於某種原因running標誌「神奇地」變成false(這個原因將是你的信號處理程序)。