2013-03-05 3 views
3

Я сделал сервер, который считывает данные с клиента, и я использую boost :: asio async_read_some для чтения данных, и я сделал одну функцию обработчика и здесь _ioService-> poll() будет запускать цикл обработки событий для выполнения готовых обработчиков. В обработчике _handleAsyncReceive я освобождаю buf, который назначен в receiveDataAsync. BufferSize 500. код выглядит следующим образом:Высокое потребление процессора и памяти при использовании boost :: asio 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; 
} 

Здесь проблема буфера выделяется в гораздо более быстрыми темпами, по сравнению с открепление и поэтому использование памяти будет идти на высоком уровне с экспоненциальной скоростью и в какой-то момент времени он будет потреблять вся память и система будут застревать. Использование процессора также составит около 90%. Как уменьшить потребление памяти и процессора?

ответ

2

У вас есть утечка памяти. io_service опрос не гарантирует, что он отправит ваш _handleAsyncReceive. Он может отправить другое событие (например, принять), поэтому ваша память на char *buf будет потеряна. Мое предположение, что вы вызываете receiveDataAsync из цикла, но его не нужно - утечка будет существовать в любом случае (с другой скоростью утечки).

Его лучше, если вы следуете asio examples и работаете с предложенными шаблонами, а не создавайте свои собственные.

2

Вы можете использовать обертку вокруг буфера, который также называется круговым буфером. Boost имеет доступную версию шаблона кругового буфера. Вы можете прочитать об этом here. Идея этого в том, что когда она заполняется, она крутится к началу, где она будет хранить вещи. Вы можете сделать то же самое с другими структурами или массивами. Например, в настоящее время я использую байтовый массив для этой цели в своем приложении.

Преимущество использования выделенного большого кругового буфера для хранения сообщений заключается в том, что вам не нужно беспокоиться о создании и удалении памяти для каждого нового входящего сообщения. Это позволяет избежать фрагментации памяти, что может стать проблемой ,

Чтобы определить соответствующий размер кругового буфера, вам нужно подумать о максимальном количестве сообщений, которые могут войти и находятся на некотором этапе обработки одновременно; умножьте это число на средний размер сообщений, а затем умножьте на коэффициент выдумки, равный 1,5. Средний размер сообщения для моего приложения составляет менее 100 байт. Размер моего буфера составляет 1 мегабайт, что позволит не менее 10 000 сообщений накапливаться без влияния на буфер вокруг буфера. Но если более 10000 сообщений накапливались без полной обработки, то циклический буфер был бы нецелесообразным, и программу пришлось бы перезапустить. Я думал о сокращении размера буфера, потому что система, вероятно, была бы мертва задолго до того, как она ударила отметку в 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 не исчерпывает работу, и поток обработки просто ждет событий. Таким образом, довольно часто начинать настройку асинхронных цепей, а затем обрабатывать цикл событий io_service в гораздо более высокой области.

С другой стороны, если определено, что TCPSocket::receiveDataAsync() должен обработать цикл событий блокирующим образом, а затем рассмотреть возможность использования синхронных операций.

Смежные вопросы