¿Cómo realizo una lectura sin bloqueo usando asio?
-
19-08-2019 - |
Pregunta
Estoy intentando usar boost :: asio para leer y escribir desde un dispositivo en un puerto serie. Tanto boost :: asio: read () como boost :: asio :: serial_port :: read_some () se bloquean cuando no hay nada que leer. En cambio, me gustaría detectar esta condición y escribir un comando en el puerto para iniciar el dispositivo.
¿Cómo puedo detectar que no hay datos disponibles?
Si es necesario, puedo hacer todo de forma asincrónica, prefiero evitar la complejidad adicional si puedo.
Solución
Tienes un par de opciones, en realidad. Puede usar la función async_read_some
integrada 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 efectivamente está "bloqueado", ya que ninguno de estos llamará a la devolución de llamada a menos que (1) se hayan leído los datos o (2) se produzca 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ás con dos devoluciones de llamada con un comportamiento similar. Si ya sea " leer " o el "tiempo de espera" la devolución de llamada se dispara con un error, sabes que es el perdedor de la carrera. Si cualquiera de los dos dispara sin error, entonces sabe que es el ganador de la carrera (y debe cancelar la otra llamada). En el lugar donde hubiera tenido su llamada de bloqueo a read_some
, ahora tendrá una llamada a io_svc.run ()
. Su función seguirá bloqueándose como antes cuando llama a ejecutar
, 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 unos pocos ajustes aquí y allá para satisfacer sus necesidades específicas. ¡Espero que esto ayude!
Otra nota: no se necesitaron subprocesos adicionales para manejar las devoluciones de llamada. Todo se maneja dentro de la llamada a run ()
. No estoy seguro si ya lo sabía ...
Otros consejos
En realidad es mucho más simple de lo que las respuestas aquí han implicado, y puedes hacerlo sincrónicamente:
Suponga que su lectura de bloqueo es algo como esto:
size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
Luego lo reemplaza 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 recibir_de que tienen casi todos los métodos de envío / recepción. Desafortunadamente toman un argumento de banderas, pero 0 parece funcionar bien.
Debe usar la función libre asio :: async_read.