提问人:Rick 提问时间:9/20/2023 更新时间:9/20/2023 访问量:58
以同步方式将全双工串行端口与 ASIO(或其他库)一起使用
Using Full-Duplex Serial Port with ASIO (or other libraries) in a Synchronous Manner
问:
我正在处理一个项目,我需要通过串行端口与设备进行通信。我一直在考虑使用 ASIO 库来处理通信,但我有几个问题:
- 串行端口本质上是全双工的吗?我可以同时读取和写入端口而不会出现任何问题吗?
- 如果我使用 ASIO,是否可以在不使用互斥锁的情况下从两个不同的线程对同一个 asio::serial_port 对象执行同步读取和写入?具体来说,我想要一个线程专门用于阅读,另一个线程专门用于写作。需要注意的是,只有一个线程会写入,一个线程会读取,因此不可能有多个读取或写入,但同时读取和写入应该是可能的。
- 如果 ASIO 不是最佳选择,您是否推荐用于 C++ 中的同步串行端口通信的其他库?
现在,我使用互斥锁保护 asio::serial_port,因此只能进行一次读取或写入。 我正在以同步方式使用 ASIO。
答:
0赞
Jonathan
9/20/2023
#1
Asio 已经线程化了。它使用线程来防止阻塞,这是库的目的(异步输入输出)。RS232 在设计上是全双工的。该协议在端口上同时处理发送和接收。
评论
2赞
sehe
9/20/2023
Asio 不是线程的。显然,它允许线程。但是,使用异步比在同一端口对象上正确协调不同的线程要容易得多。
0赞
Jonathan
9/20/2023
@sehe 感谢您的澄清。无论如何,我从未使用过没有设置后台线程来处理读/写操作的 ASIO,所以这就是我感到困惑的地方。
0赞
sehe
9/20/2023
在答案中添加了单线程异步全双工串行 IO 的示例
0赞
sehe
9/20/2023
#2
是的,您可以通过同步对 IO 对象的多线程访问来以复杂的方式做到这一点:
(todo想出复杂的例子)
更简单的单线程异步
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <deque>
#include <iomanip>
#include <iostream>
namespace asio = boost::asio;
using sp = asio::serial_port;
using boost::system::error_code;
using namespace std::literals;
struct Example {
Example(asio::any_io_executor ex, std::string dev) : sp_(ex, dev) {
readLoop();
}
void Send(std::string cmd) {
std::cerr << "Queue: " << quoted(cmd) << std::endl;
outbox_.push_back(std::move(cmd));
if (outbox_.size() == 1)
writeLoop();
}
void Stop() {
sp_.cancel();
}
private:
void readLoop() {
asio::async_read_until( //
sp_, asio::dynamic_buffer(incoming_), "\n", [this](error_code ec, size_t len) {
std::cerr << "Read: " << len << " (" << ec.message() << ")" << std::endl;
if (!ec.failed()) {
Send("echo: "s + incoming_.substr(0, len));
incoming_.erase(0, len);
readLoop();
}
});
}
void writeLoop() {
if (outbox_.empty())
return;
asio::async_write(sp_, asio::buffer(outbox_.front()), [this](error_code ec, size_t len) {
std::cerr << "Wrote: " << len << " (" << ec.message() << ")" << std::endl;
if (!ec.failed()) {
outbox_.pop_front();
writeLoop();
}
});
}
asio::serial_port sp_;
std::string incoming_;
std::deque<std::string> outbox_;
};
int main(int argc, char** argv) {
asio::io_context ioc;
Example drone(ioc.get_executor(), (argc > 1 ? argv[1] : "/dev/stty0"));
ioc.run_for(30s);
// not really needed, but to show graceful shutdown
drone.Stop();
ioc.run();
}
现场演示:
评论
1赞
sehe
9/20/2023
老实说,由于 asio::serial_port
的线程安全性,我认为我无法使多线程方法工作:这意味着两个线程都在锁下阻塞。(如果你想看看我写了什么 - 这是损坏的代码:coliru.stacked-crooked.com/a/9b5104a6f55e6dd2)
评论