std :: string испорчен при использовании его как хранилища внутри (boost.) async_read_some - PullRequest
0 голосов
/ 24 февраля 2011

Я использую async_read_some для чтения данных из порта, который сохранен в char [] с именем _data.Его размер буфера достаточно велик для каждого запроса:

void start() {

    socket_.async_read_some(boost::asio::buffer(data_,BUFFERSIZE),make_custom_alloc_handler(allocator_,boost::bind(&attentionSession::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));

}

void handle_read(const boost::system::error_code& error, size_t bytes_transferred) {

    string ip = socket_.remote_endpoint().address().to_string();
    log->processData(data_,ip,"memory");
    strcpy(data_,"");

}

processData добавляет некоторую дополнительную информацию (например, метку времени и т. Д.) К запросу, копируя его в другой недавно выделенный символ *.Затем этот char [] отправляется в writeToMemory (char *), чтобы добавить этот char * к std :: string memoryBuffer:

void writeCacheToFile() {

    // This function writes buffer data to the log file

    char* temp = new char[memoryBuffer.length() + 1];
    strcpy(temp, memoryBuffer.c_str());
    writeToFile(temp);
    delete[] temp;
    memoryBuffer.clear();

}

void writeToMemory(char* data) {

    int maxSize = 1024;

     // Checks if char* data would 'fit' into the pre-defined maxSize

    if ((strlen((const char*)data) + memoryBuffer.length()) >= maxSize) {
        writeCacheToFile(); // If not the cache memoryBuffer is saved first
    }

    memoryBuffer.append((const char*) data);

    cout << memoryBuffer.length() << endl;

}

. Это работает, но если есть постоянные запросы (бомбардировка его запросами)запутаться.Как вы можете видеть выше в функции writeToMemory (), я добавил строку, чтобы вывести текущую длину memoryBuffer, и именно здесь я думаю, что она имеет отношение к безопасности потока std :: strings:

96
188
284
3639
94
190
286
2591
102
198
294
388
484
2591
96
2591
96
190
286
2591

Длина каждого (обработанного процессом processData ()) запроса составляет 96 символов.Но здесь длина memoryBuffer просто увеличивается и уменьшается - некоторые длины даже больше, чем maxSize (1024 символа).

РЕДАКТИРОВАТЬ: Сэм указал, что я должен добавить еще немного кода.Вот как я запускаю io_service:

boost::asio::io_service ioService;
boost::scoped_ptr<boost::thread> ioServiceThread; 
server_class server (ioService,PORT); // Create server instance
ioServiceThread.reset (new boost::thread ( boost::bind ( &boost::asio::io_service::run, &ioService  ) ) ); 
// Only one threaded io_service (to receive user inputs in main() function)

И это функция async_acceptor после выполнения запроса:

typedef boost::shared_ptr<session_class> session_ptr;

void handleAccept(session_ptr thisSession, const boost::system::error_code& error) {
    if (!error) {
      thisSession->start(); // Calls the start() function above
      thisSession.reset(new session(ioService,LOGGING_CLASS));
      acceptor.async_accept(thisSession->socket(),boost::bind(&server_class::handleAccept, this, thisSession, PLACEHOLDER_ERROR));
    }
 }

Сеансовый класс содержит функции start () и handle_read (x,у) упоминалось выше.LOGGING_CLASS предоставляет класс для записи файлов журнала (содержит функции writeCacheToFile () и writeToMemory (char *)).log (упомянутый выше) является разновидностью этого класса.

EOE: END OF EDIT

Если я пытаюсь исправить аутсорсинг кэширующей части (добавление полученного символа * кstd :: string) с boost :: threads заканчивается полностью перепутанным memoryBuffer

Действительно ли это потокобезопасность std :: strings или чего-то еще, что я пропустил?

Спасибо заВаша помощь заранее!:)

Пол

Ответы [ 3 ]

1 голос
/ 26 февраля 2011

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

  1. Вы не проверяете параметр bytes_transferred в обработчике async_read_some.Это очень важно , потому что даже если вы скажете ему прочитать n байт, он может вернуться при чтении n - x байтов, где x <= n.Поскольку в документации указано , вам следует рассмотреть возможность использования одной из составных операций, таких как async_read free function .
  2. вы используете пользовательских выделений памяти для ваших операций асинхронного чтения, предположительно на основе предоставленного примера .Зачем вам это нужно?
  3. время жизни буфера.Пример: ваши буферы остаются в области действия, пока не будет вызван обработчик async_read?
  4. время жизни объекта.Пример: вы правильно используете shared_ptr?Является ли io_service областью действия для всего цикла обработки событий?
  5. Вы используете один io_service на процесс или один на поток?
  6. зачем вам потоки?Как правило, сначала проще понять асинхронное программирование в однопотоковом контексте.

Все это очень важные концепции, которые необходимо понять при использовании Boost.Asio.Часть отладки сводится к воспринимаемой проблеме в меньшем воспроизводителе.Это полезно как при переполнении стека, так и для того, чтобы стать хорошим программистом в целом.Это поможет вам понять проблему, а также поможет us помочь вам найти ее.Я настоятельно рекомендую вам потратить некоторые усилия на создание меньшего воспроизводимого примера, который мы можем скомпилировать.Если это невозможно, рассмотрите возможность использования нескольких потоков только после того, как вы доказали, что однопоточный сценарий работает.

1 голос
/ 27 февраля 2011

Ну, проблема не была ни в Boost, ни в Boost :: Asio.Это был метод, которым я тестировал свое приложение:

Я использовал nc (Netcat) для тестирования производительности и функций моего приложения с помощью этой команды

nc localhost 4450 <testfile

, где testfile содержал длину 36 символовтестовая строка.Netcat был не просто медленным - он был источником этой проблемы.

После того, как я изменил свою стратегию и написал небольшое приложение boost :: asio, чтобы отправлять те же запросы моему приложениюработал.Быстро, легко и без проблем.

Я усвоил урок: никогда больше не используйте Netcat для стресс-тестов!

0 голосов
/ 24 февраля 2011

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

Я полагаю, что когда вы нажмете это, у вас будут новые вызовы handle_read до того, как старые вызовы вернутся.handle_read не является потокобезопасным.Помимо всего прочего,

strcpy(data_,"");

доставит вам неприятности, когда все копии пытаются скопировать в данные _.

Вам необходимо сделать handle_read потоко-безопасным.

...