我正在使用 asio 同步套接字从后台线程通过 TCP 读取数据。这被封装在“服务器”类中。
但是,我希望线程在调用此类的析构函数时退出。问题是对任何读取函数的调用都会阻塞,因此线程不能轻易终止。在 Win32 中,有一个 API:WaitForMultipleObjects
它可以做我想要的。
我如何通过 boost 达到类似的效果?
我正在使用 asio 同步套接字从后台线程通过 TCP 读取数据。这被封装在“服务器”类中。
但是,我希望线程在调用此类的析构函数时退出。问题是对任何读取函数的调用都会阻塞,因此线程不能轻易终止。在 Win32 中,有一个 API:WaitForMultipleObjects
它可以做我想要的。
我如何通过 boost 达到类似的效果?
我发现没有简单的方法可以做到这一点。据说有取消win32 IOCP的方法,但是在windows XP上就不行了。MS确实为windows vista和7修复了它。取消asio的推荐方法async_read
是async_write
关闭套接字。
[析构函数] 等待完成处理程序
[completion] 如果由于套接字关闭而拆除并且我们刚刚失败,则通知析构函数完成处理程序已完成。
如果您选择实现这一点,请小心。关闭套接字非常简单。然而,“等待完成处理程序”是一种轻描淡写的说法。当服务器的线程和它的析构函数交互时,可能会发生一些微妙的极端情况和竞争条件。
这非常微妙,我们构建了一个完成包装器(类似于io_service::strand
处理同步取消所有待处理的完成回调。
在我们的应用程序中,我们设置了“终止”条件,然后使用自连接到线程正在侦听的端口,以便它唤醒,记录终止条件并终止。
您还可以检查 boost 实现——如果他们只是在套接字上进行简单的读取(即,不使用内部内部的 WaitForMultipleObjects 之类的东西),那么您可能会得出结论,没有任何东西可以简单而干净地解除阻塞线程。如果他们正在等待多个对象(或完成端口),您可以四处挖掘以查看唤醒阻塞线程的能力是否暴露在外部。
最后,您可以终止线程 - 但您必须在 boost 之外执行此操作,并了解后果,例如悬空或泄漏资源。如果您正在关闭,这可能不是问题,具体取决于该线程在做什么。
最好的方法是创建一个socketpair()
, (无论用什么boost::asio
说法),将阅读器端添加到事件循环中,然后关闭编写器端。您将立即被该套接字上的 eof 事件唤醒。
然后线程必须自动关闭。
线程的生成器应在其析构函数中,具有以下内容:
~object()
{
shutdown_queue.shutdown(); // ask thread to shut down
thread.join(); // wait until it does
}
boost::system::error_code _error_code;
client_socket_->shutdown(client_socket_->shutdown_both, _error_code);
上面的代码帮助我立即关闭同步读取。
使用 socket.cancel(); 结束当前阻塞在套接字上的所有异步操作。客户端套接字可能需要在循环中被杀死。我从来不需要以这种方式关闭服务器,但您可以使用 shared_from_this() 并在循环中运行 cancel()/close() 类似于 boost chat 示例 async_writes 对所有客户端的方式。