简体   繁体   中英

High CPU and Memory Consumption on using boost::asio async_read_some

I have made a server that reads data from client and I am using boost::asio async_read_some for reading data, and I have made one handler function and here _ioService->poll() will run event processing loop to execute ready handlers. In handler _handleAsyncReceive I am deallocating the buf that is assigned in receiveDataAsync. bufferSize is 500. Code is as follows:

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;
}

Here the problem is buffer is allocated at much faster rate as compare to deallocation so memory usage will go high at exponential rate and at some point of time it will consume all the memory and system will be stuck. CPU usage will also be around 90%. How can I reduce the memory and CPU consumption?

You have a memory leak. io_service poll does not guarantee that it with dispatch your _handleAsyncReceive . It can dispatch other event (eg an accept), so your memory at char *buf is lost. My guess you are calling receiveDataAsync from a loop, but its not necessary - leak will exist in any case (with different leak speed).

Its better if you follow asio examples and work with suggested patterns rather than make your own.

You might consider using a wrap around buffer, which is also called a circular buffer. Boost has a template circular buffer version available. You can read about it here. The idea behind it is that when it becomes full, it circles around to the beginning where it will store things. You can do the same thing with other structures or arrays as well. For example, I currently use a byte array for this purpose in my application.

The advantage of using a dedicated large circular buffer to hold your messages is that you don't have to worry about creating and deleting memory for each new message that comes in. This avoids fragmentation of memory, which could become a problem.

To determine an appropriate size of the circular buffer, you need to think about the maximum number of messages that can come in and are in some stage of being processed simultaneously; multiply that number by the average size of the messages and then multiply by a fudge factor of perhaps 1.5. The average message size for my application is under 100 bytes. My buffer size is 1 megabyte, which would allow for at least 10,000 messages to accumulate without it affecting the wrap around buffer. But, if more than 10,000 messages did accumulate without being completely processed, then the circular buffer would be unuseable and the program would have to be restarted. I have been thinking about reducing the size of the buffer because the system would probably be dead long before it hit the 10,000 message mark.

As PSIAlt suggest, consider following the Boost.Asio examples and build upon their patterns for asynchronous programming.

Nevertheless, I would suggest considering whether multiple read calls need to be queued onto the same socket. If the application only allows for a single read operation to be pending on the socket, then resources are reduced:

  • There is no longer the scenario where there are an excessive amount of handlers pending in the io_service .
  • A single buffer can be preallocated and reused for each read operation. For example, the following asynchronous call chain only requires a single buffer, and allows for the concurrent execution of starting an asynchronous read operation while the previous data is being emitted on the Qt signal, as QString performs deep-copies.

     TCPSocket::start() { receiveDataAsync(...) --. } | .---------------' | .-----------------------------------. vv | 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(); 

It is important to identify when and where the io_service 's event loop will be serviced. Generally, applications are designed so that the io_service does not run out of work, and the processing thread is simply waiting for events to occur. Thus, it is fairly common to start setting up asynchronous chains, then process the io_service event loop at a much higher scope.

On the other hand, if it is determined that TCPSocket::receiveDataAsync() should process the event loop in a blocking manner, then consider using synchronous operations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM