2016-03-05 203 views
1

我有我的應用程序的主迴路控制,我做啓動一個線程來處理ASIO工作如下:捕獲異常

void AsioThread::Run() 
{ 
    try 
    { 
     /* 
     * Start the working thread to io_service.run() 
     */ 
     boost::asio::io_service::work work(ioService); 
     boost::thread t(boost::bind(&boost::asio::io_service::run, &ioService)); 
     t.detach(); 

     while (true) 
     { 

      // Process stuff related to protocol, calling 
      // connect_async, send_async and receive_async 
     } 
    } 
    catch (std::runtime_error &ex) 
    { 
     std::cout << "ERROR IN FTP PROTOCOL: " << ex.what() << std::endl; 
    } 
    catch (...) 
    { 
     std::cout << "UNKNOWN EXCEPTION." << std::endl; 
    } 

在異步操作,處理程序被調用,有時我也引發異常這些處理程序,如:

void AsioThread::ReceiveDataHandler(const boost::system::error_code& errorCode, std::size_t bytesTransferred) 
{ 
     std::cout << "Receive data handler called. " << bytesTransferred << " bytes received." << std::endl; 

     if (errorCode) 
     { 
      std::cout << "Error receiving data from server." << std::endl; 
     } 

     rxBufferSize = bytesTransferred; 

     /* 
     * Check for response 
     */ 
     std::string msg(rxBuffer); 
     if (msg.substr(0, 3) != "220") 
       throw std::runtime_error("Error connecting to FTP Server"); 
    } 

我的問題是,異步處理器(AsioThread::ReceiveDataHandler)內拋出的異常不是由主處理循環try...catch塊逮住在AsioThread::Run。當然,這是因爲工作線程t在另一個線程上,並且在運行時導致執行錯誤。

如何從分離的boost::asio::io_service::work線程收到異常?我怎樣才能構建我的代碼來使這個邏輯起作用?

感謝您的幫助。

+1

爲什麼你有一個單獨的while (true)事件循環?你可以使用asio事件循環來做所有事情?在那種情況下加入線程。如果可能會失效,請創建一個io_service :: work對象。 – Ralf

+0

我需要事件循環,因爲它可以用作網絡邏輯的狀態處理器...不能沒有它。 – Mendes

+0

不知道我是否理解你,但通常在異步回調中處理協議狀態。這不需要有一個單獨的事件循環。例如,在async_connect之後的回調函數中,你將被連接等。事件處理程序開始下一個異步操作,例如async_connect處理程序也可以啓動讀取和寫入。 – Ralf

回答

2

您可以在工作線程中捕獲異常,將它們保存到由兩個線程共享的隊列變量中,並在主線程中定期檢查該隊列。

要使用隊列,您需要先將異常轉換爲常用類型。您可以使用std::exceptionstring或任何最適合您的情況。如果您絕對需要保留原始異常類的信息,則可以使用boost::exception_ptr

變量,你需要(這些的AsioThread可能是成員):

boost::mutex queueMutex; 
std::queue<exceptionType> exceptionQueue; 

運行該功能在工作線程:

void AsioThread::RunIoService(){ 
    try{ 
     ioService.run(); 
    } 
    catch(const exceptionType& e){ 
     boost::lock_guard<boost::mutex> queueMutex; 
     exceptionQueue.push(e); 
    } 
    catch(...){ 
     boost::lock_guard<boost::mutex> queueMutex; 
     exceptionQueue.push(exceptionType("unknown exception")); 
    } 
} 

啓動輔助線程這樣的:

boost::thread t(boost::bind(&AsioThread::RunIoService, this)); 
t.detach(); 

在主線程中:

while(true){ 
    // Do something 

    // Handle exceptions from the worker thread 
    bool hasException = false; 
    exceptionType newestException; 
    { 
     boost::lock_guard<boost::mutex> queueMutex; 
     if(!exceptionQueue.empty()){ 
      hasException = true; 
      newestException = exceptionQueue.front(); 
      exceptionQueue.pop(); 
     } 
    } 
    if(hasException){  
     // Do something with the exception 
    } 
} 

This blog post實現了一個線程安全隊列,您可以使用它來簡化保存異常;在這種情況下,你不需要單獨的互斥體,因爲這將在隊列類中。

+0

這是一個好主意,但我沒有編碼工作線程......它由'boost :: asio'處理。請參閱'boost :: thread t(boost :: bind(&boost :: asio :: io_service :: run,&ioService));'...... – Mendes

+0

@Mendes雖然您可能無法控制io_service :: run() ',你可以控制線程的入口點。考慮創建一個在try/catch塊內調用'io_service.run()'的函數,根據需要傳播異常,並將此函數指定爲線程入口點。 –

+0

@Mendes我改變了我的答案,它現在應該在你的情況下工作。 –