boost - ¿Cómo realizo una lectura sin bloqueo usando asio?
serial-port boost-asio (3)
Estoy intentando utilizar boost :: asio para leer y escribir desde un dispositivo en un puerto serie. Both boost :: asio: read () y boost :: asio :: serial_port :: read_some () bloquean cuando no hay nada que leer. En cambio, me gustaría detectar esta condición y escribir un comando en el puerto para poner en marcha el dispositivo.
¿Cómo puedo detectar que no hay datos disponibles?
Si es necesario, puedo hacer todo de manera asincrónica, prefiero evitar la complejidad adicional si puedo.
Tienes que usar la función libre asio :: async_read.
Tienes un par de opciones, en realidad. Puede usar la función async_read_some
del puerto serie, o puede usar la función independiente boost::asio::async_read
(o async_read_some
).
Todavía se encontrará con la situación en la que está efectivamente "bloqueado", ya que ninguno de ellos llamará a la devolución de llamada a menos que (1) los datos se hayan leído o (2) se haya producido un error. Para evitar esto, querrá usar un objeto deadline_timer
para establecer un tiempo de espera. Si el tiempo de espera se dispara primero, no hay datos disponibles. De lo contrario, habrá leído los datos.
La complejidad añadida no es realmente tan mala. Terminará con dos devoluciones de llamada con un comportamiento similar. Si la devolución de llamada de "lectura" o "tiempo de espera" se activa con un error, sabrá que es el perdedor de la carrera. Si alguno de ellos se dispara sin un error, entonces sabrá que es el ganador de la carrera (y debe cancelar la otra llamada). En el lugar donde habría tenido su llamada de bloqueo para read_some
, ahora tendrá una llamada a io_svc.run()
. Su función todavía se bloqueará como antes cuando llama a run
, pero esta vez usted controla la duración.
Aquí hay un ejemplo:
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
}
Eso debería comenzar con solo algunos ajustes aquí y allá para satisfacer sus necesidades específicas. ¡Espero que esto ayude!
Otra nota: No se necesitaron hilos adicionales para gestionar las devoluciones de llamadas. Todo se maneja dentro de la llamada a run()
. No estoy seguro de si ya sabías esto ...
Es en realidad mucho más simple de lo que implican las respuestas aquí, y puedes hacerlo sincrónicamente:
Supongamos que su lectura de bloqueo fue algo como esto:
size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
Luego lo reemplazas con
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);
Utiliza la forma alternativa sobrecargada de receive_from que tienen casi todos los métodos send / receive. Desafortunadamente toman un argumento de banderas pero 0 parece funcionar bien.