2013-03-05 369 views
3

我已經做了一個服務器,從客戶端讀取數據,我使用boost :: asio async_read_some讀取數據,並且我已經創建了一個處理函數,並且在這裏_ioService-> poll()將運行事件處理循環來執行就緒處理程序。在處理程序_handleAsyncReceive中,我取消了在receiveDataAsync中分配的buf。緩衝區大小爲500 代碼如下:使用boost :: asio時的高CPU和內存消耗async_read_some

bool 
TCPSocket::receiveDataAsync(unsigned int bufferSize) 
{ 
    char *buf = new char[bufferSize + 1]; 

    try 
    { 
     _tcpSocket->async_read_some(boost::asio::buffer((void*)buf, bufferSize), 
            boost::bind(&TCPSocket::_handleAsyncReceive, 
                this, 
                buf, 
                boost::asio::placeholders::error, 
                boost::asio::placeholders::bytes_transferred)); 

      _ioService->poll(); 

    } 
    catch (std::exception& e) 
    { 
     LOG_ERROR("Error Receiving Data Asynchronously"); 
     LOG_ERROR(e.what()); 
     delete [] buf; 
     return false; 
    } 

    //we dont delete buf here as it will be deleted by callback _handleAsyncReceive 
    return true; 
} 


void 
TCPSocket::_handleAsyncReceive(char *buf, const boost::system::error_code& ec, size_t size) 
{ 
    if(ec) 
    { 
     LOG_ERROR ("Error occurred while sending data Asynchronously."); 
     LOG_ERROR (ec.message()); 
    } 
    else if (size > 0) 
    { 
     buf[size] = '\0'; 
     LOG_DEBUG("Deleting Buffer"); 
     emit _asyncDataReceivedSignal(QString::fromLocal8Bit(buf)); 
    } 
    delete [] buf; 
} 

這裏的問題是緩衝區以更快的速率比較釋放這樣的內存使用率會高以指數的速度和在某個時候,它會消耗分配所有的內存和系統都會卡住。 CPU使用率也將在90%左右。我如何減少內存和CPU消耗?

回答

2

您有內存泄漏。 io_service民意調查不保證它與調度你的_handleAsyncReceive。它可以派遣其他事件(例如接受),因此你的記憶在char *buf丟失。我猜你從迴路呼叫receiveDataAsync,但它不是必需的 - 任何情況下泄漏都會存在(具有不同的泄漏速度)。

它更好,如果你按照asio examples並使用建議的模式,而不是自己做。

2

您可能會考慮使用環繞緩衝區,也稱爲循環緩衝區。 Boost有一個可用的模板循環緩衝區版本。你可以閱讀它here.它背後的想法是,當它變滿時,它會繞着它開始存儲的東西。您也可以對其他結構或數組做同樣的事情。例如,我目前在我的應用程序中爲此使用了一個字節數組。

使用專用的大型循環緩衝區來保存消息的優點是,您不必擔心爲進入的每條新消息創建和刪除內存。這可以避免內存碎片,這可能會成爲問題。

要確定循環緩衝區的合適大小,您需要考慮可以進入並處於某個同時處理階段的最大消息數量;將該數字乘以消息的平均大小,然後乘以大約1.5的模糊因子。我的應用程序的平均消息大小不到100字節。我的緩衝區大小爲1兆字節,這將允許至少10,000條消息累積,而不會影響環繞緩衝區。但是,如果超過10,000條消息沒有完全處理就累積起來,那麼循環緩衝區將不可用,並且程序將不得不重新啓動。我一直在考慮減小緩衝區的大小,因爲系統可能會在達到10,000條消息標記之前就已經很長時間了。

2

由於PSIAlt建議,請考慮遵循Boost.Asio examples並基於它們的異步編程模式。

不過,我會建議考慮是否有多個讀取調用需要排隊到同一個套接字上。如果應用程序只允許對要在插座上未決單個讀操作,則資源被減少:

  • 不再有其中存在在io_service未決處理過量的情況。
  • 可以預先分配單個緩衝區,併爲每個讀取操作重新使用。例如,以下異步調用鏈只需要一個緩衝區,並允許在Qt信號上發送先前數據時同時開始異步讀取操作,因爲QString執行深度複製。

    TCPSocket::start() 
    { 
        receiveDataAsync(...) --. 
    }       | 
          .---------------' 
          | .-----------------------------------. 
          v v         | 
    TCPSocket::receiveDataAsync(...)     | 
    {             | 
        _tcpSocket->async_read_some(_buffer); --.  | 
    }           |  | 
          .-------------------------------'  | 
          v          | 
    TCPSocket::_handleAsyncReceive(...)    | 
    {             | 
        QString data = QString::fromLocal8Bit(_buffer); | 
        receiveDataAsync(...); --------------------------' 
        emit _asyncDataReceivedSignal(data); 
    } 
    
    ... 
    
    tcp_socket.start(); 
    io_service.run(); 
    

它以確定在何時何地io_service的事件循環將服務是很重要的。通常,應用程序的設計使io_service不會耗盡工作,並且處理線程只是等待事件發生。因此,開始設置異步鏈是相當常見的,然後在更高的範圍內處理事件循環。

另一方面,如果確定TCPSocket::receiveDataAsync()應以阻塞方式處理事件循環,則考慮使用同步操作。