我一直致力于通过使用boost :: asio :: basic_serial_port学习基本的串行终端示例来实现半双工串行驱动程序: http://lists.boost.org/boost-users/att-41140/minicom.cpp
我需要异步读取但仍然检测处理程序何时在主线程中完成,因此我使用boost:bind在asda_read_some中传递一个带有几个额外引用参数的回调函数。永远不会调用处理程序但是如果我用read_some函数替换async_read_some函数,它会返回没有问题的数据。
我相信我满足了这个函数调用处理程序的所有必要条件,因为它们与asio :: read一样,返回一些函数:
有没有人知道我是否缺少异步读取特有的另一个假设,或者我是不是正确设置了io_service?
以下是我如何使用async_read_some(http://www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/basic_serial_port/async_read_some.html)代码的示例:
void readCallback(const boost::system::error_code& error, size_t bytes_transfered, bool & finished_reading, boost::system::error_code& error_report, size_t & bytes_read)
{
std::cout << "READ CALLBACK\n";
std::cout.flush();
error_report = error;
bytes_read = bytes_transfered;
finished_reading = true;
return;
}
int main()
{
int baud_rate = 115200;
std::string port_name = "/dev/ttyUSB0";
boost::asio::io_service io_service_;
boost::asio::serial_port serial_port_(io_service_,port_name);
serial_port_.set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
boost::thread service_thread_;
service_thread = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_));
std::cout << "Starting byte read\n";
boost::system::error_code ec;
bool finished_reading = false;
size_t bytes_read;
int max_response_size = 8;
uint8_t read_buffer[max_response_size];
serial_port_.async_read_some(boost::asio::buffer(read_buffer, max_response_size),
boost::bind(readCallback,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
finished_reading, ec, bytes_read));
std::cout << "Waiting for read to finish\n";
while (!finished_reading)
{
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
}
std::cout << "Finished byte read: " << bytes_read << "\n";
for (int i = 0; i < bytes_read; ++i)
{
printf("0x%x ",read_buffer[i]);
}
}
结果是回调没有打印出任何内容,而while!finished循环永远不会完成。
以下是我使用阻塞read_some函数的方法(boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/basic_serial_port/read_some.html):
int main()
{
int baud_rate = 115200;
std::string port_name = "/dev/ttyUSB0";
boost::asio::io_service io_service_;
boost::asio::serial_port serial_port_(io_service_,port_name);
serial_port_.set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
boost::thread service_thread_;
service_thread = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_));
std::cout << "Starting byte read\n";
boost::system::error_code ec;
int max_response_size = 8;
uint8_t read_buffer[max_response_size];
int bytes_read = serial_port_.read_some(boost::asio::buffer(read_buffer, max_response_size),ec);
std::cout << "Finished byte read: " << bytes_read << "\n";
for (int i = 0; i < bytes_read; ++i)
{
printf("0x%x ",read_buffer[i]);
}
}
这个版本打印我发送的1到8个字符,阻塞直到至少发送一个。
答案 0 :(得分:1)
请注意boost::bind
copies its arguments。如果您想通过引用传递参数,请使用boost::ref
(或std::ref
)包装它:
boost::bind(readCallback, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, boost::ref(finished_reading), ec, bytes_read));
(但是,严格来说,您传递给另一个帖子的bool
变量存在竞争条件。更好的解决方案是使用std::atomic_bool
。)
答案 1 :(得分:1)
代码不保证io_service
正在运行。 io_service::run()
会在以下情况下返回:
io_service
已停止。在这种情况下,可以在启动serial_port::async_read_some()
操作之前创建service_thread_
并调用io_service::run()
,将工作添加到io_service
。因此,service_thread_
可以立即从io_service::run()
返回。要解决此问题,请:
io_service::run()
。service_thread_
之前创建一个io_service::work
对象。 work
对象会阻止io_service
停止工作。此answer可以更深入地了解io_service::run()
的行为。
要注意的其他一些事项,并在Igor's answer上展开:
boost::bind()
按值复制其参数。要通过引用传递参数,请使用boost::ref()
或boost::cref()
boost::bind(..., boost::ref(finished_reading), boost::ref(ec),
boost::ref(bytes_read));
需要添加同步以保证主线程中finished_reading
的内存可见性。对于异步操作,Boost.Asio将guarantee适当的内存屏障以确保正确的内存可见性(有关详细信息,请参阅此answer)。在这种情况下,主线程中需要memory barrier以保证主线程观察到其他线程对finished_reading
的更改。请考虑使用Boost.Thread同步机制(如boost::mutex
)或Boost.Atomic的atomic objects或thread and signal fences。