Как выполнить чтение без блокировки с помощью asio?

Я пытаюсь использовать boost:: asio для чтения и записи с устройства на последовательном порту. Оба блока boost:: asio: read() и boost:: asio:: serial_port:: read_some(), когда читать нечего. Вместо этого я хотел бы обнаружить это условие и написать команду порту для запуска устройства.

Как я могу обнаружить, что данные не доступны?

При необходимости я могу делать все асинхронно, я бы просто избегал лишней сложности, если мог.

Ответ 1

На самом деле у вас есть несколько вариантов. Вы можете использовать встроенную функцию async_read_some для последовательного порта, или вы можете использовать автономную функцию boost::asio::async_read (или async_read_some).

Вы все равно столкнетесь с ситуацией, когда вы эффективно заблокированы, поскольку ни один из них не будет вызывать обратный вызов, если только (1) данные не были прочитаны, или (2) произошла ошибка. Чтобы обойти это, вы захотите использовать объект deadline_timer для установки тайм-аута. Если тайм-аут срабатывает первым, данных не было. В противном случае вы будете читать данные.

Добавленная сложность на самом деле не так уж плоха. Вы получите два обратных вызова с аналогичным поведением. Если срабатывает обратный вызов "read" или "timeout" с ошибкой, вы знаете, что это проигравший в гонке. Если кто-то стреляет без ошибки, то вы знаете это победитель гонки (и вы должны отменить другой звонок). В том месте, где у вас был бы блокирующий вызов read_some, вы теперь получите вызов io_svc.run(). Ваша функция по-прежнему будет блокироваться по-прежнему, когда она вызывает run, но на этот раз вы контролируете продолжительность.

Вот пример:

void foo()
{
  io_service     io_svc;
  serial_port    ser_port(io_svc, "your string here");
  deadline_timer timeout(io_svc);
  unsigned char  my_buffer[1];
  bool           data_available = false;

  ser_port.async_read_some(boost::asio::buffer(my_buffer),
      boost::bind(&read_callback, boost::ref(data_available), boost::ref(timeout),
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred));
  timeout.expires_from_now(boost::posix_time::milliseconds(<<your_timeout_here>>));
  timeout.async_wait(boost::bind(&wait_callback, boost::ref(ser_port),
                  boost::asio::placeholders::error));

  io_svc.run();  // will block until async callbacks are finished

  if (!data_available)
  {
    kick_start_the_device();
  }
}

void read_callback(bool& data_available, deadline_timer& timeout, const boost::system::error_code& error, std::size_t bytes_transferred)
{
  if (error || !bytes_transferred)
  {
    // No data was read!
    data_available = false;
    return;
  }

  timeout.cancel();  // will cause wait_callback to fire with an error
  data_available = true;
}

void wait_callback(serial_port& ser_port, const boost::system::error_code& error)
{
  if (error)
  {
    // Data was read and this timeout was canceled
    return;
  }

  ser_port.cancel();  // will cause read_callback to fire with an error
}

Это должно помочь вам начать с нескольких трюков здесь и там, чтобы удовлетворить ваши конкретные потребности. Надеюсь, это поможет!

Другое примечание: для обработки обратных вызовов не требуется дополнительных потоков. Все обрабатывается при вызове run(). Не уверен, что вы уже знали об этом...

Ответ 2

Вы должны использовать бесплатную функцию asio:: async_read.

Ответ 3

На самом деле это намного проще, чем здесь подразумеваются ответы, и вы можете сделать это синхронно:

Предположим, что ваше чтение блокировки было примерно таким:

size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);

Затем вы замените его на

socket.non_blocking(true);
size_t len = 0;
error = boost::asio::error::would_block;
while (error == boost::asio::error::would_block)
    //do other things here like go and make coffee
    len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, error);
std::cout.write(recv_buf.data(), len);

Вы используете альтернативную перегруженную форму receive_from, которой обладают почти все методы отправки/получения. К сожалению, они принимают аргумент flags, но 0, похоже, работает нормально.