Wie führe ich eine nicht-blockierende Lese mit Asio?
-
19-08-2019 - |
Frage
Ich versuche Schub zu verwenden :: asio zu lesen und von einem Gerät an einer seriellen Schnittstelle zu schreiben. Beide boost :: asio: read () und boost :: asio :: serial_port :: read_some () blockieren, wenn es nichts zu lesen. Ich würde stattdessen möchte diesen Zustand erkennen und einen Befehl an den Port schreiben, um das Gerät anzukurbeln.
Wie kann ich entweder feststellen, dass keine Daten verfügbar sind?
Bei Bedarf ich alles asynchron tun kann, würde ich nur eher vermeiden, dass die zusätzliche Komplexität, wenn ich kann.
Lösung
Sie haben ein paar Optionen, eigentlich. Sie können entweder die serielle Schnittstelle des eingebauten in async_read_some
Funktion verwenden, oder Sie können den Stand-alone-Funktion boost::asio::async_read
(oder async_read_some
).
Sie werden immer noch in die Situation kommen, wo man effektiv „blockiert“ ist, da es sei denn, den Rückruf weder von diesen nennen (1) Daten gelesen wurden oder (2) ein Fehler auftritt. Um dies zu umgehen, sollen Sie ein deadline_timer
Objekt verwenden, um ein Timeout zu setzen. Wenn das Timeout erstes feuert, waren keine Daten verfügbar. Andernfalls werden Sie lesen Daten haben.
Die zusätzliche Komplexität ist nicht wirklich so schlecht. Sie werden sich mit zwei Rückrufe mit ähnlichem Verhalten beenden. Wenn entweder die „Lesen“ oder der „timeout“ Callback-Brand mit einem Fehler, Sie wissen, dass es das Rennen Verlierer. Wenn entweder ein Feuer ohne einen Fehler, dann wissen Sie es das Rennen des Siegers (und Sie sollten den anderen Anruf abzubrechen). An der Stelle, wo Sie Ihren blockierenden Anruf hätten read_some
, werden Sie nun einen Anruf müssen io_svc.run()
. Ihre Funktion wird immer noch blockiert nach wie vor, wenn es ruft run
, aber dieses Mal sind Sie steuern die Dauer.
Hier ist ein Beispiel:
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
}
Das sollten Sie mit nur ein paar Veränderungen hier und da passen Ihre spezifischen Bedürfnisse gestartet. Ich hoffe, das hilft!
Noch ein Hinweis: Keine zusätzlichen Fäden waren notwendig, Rückrufe zu handhaben. Alles ist innerhalb des Call behandelt run()
. Nicht sicher, ob Sie waren bereits bekannt, diese ...
Andere Tipps
Es ist eigentlich viel einfacher als die Antworten hier haben angedeutet, und Sie können es tun synchron:
Angenommen, Ihre Sperrung Lese so etwas wie dies:
size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
Dann ersetzen Sie es mit
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);
Sie verwenden die Alternative überlastet Form receive_from die fast alle die Sende- / Empfangsverfahren haben. Sie nehmen leider ein Argument flags aber 0 scheint gut zu funktionieren.
Sie haben die freie Funktion Asio verwenden :: async_read.