如何使用 asio 简化多线程设计?
Posted
技术标签:
【中文标题】如何使用 asio 简化多线程设计?【英文标题】:How to simplify multithreaded design using asio? 【发布时间】:2014-02-20 09:48:31 【问题描述】:这是一个很长的问题。 TL;DR:我不知道如何使用 asio 正确设计程序。
人们已经为 CLI 实用程序编写了 GUI 包装器。我正在为这样一个实用程序编写一个“代理”,它将 GUI 发出的命令中继到远程计算机上运行的实用程序的另一个副本(通过远程计算机上的另一个“代理”)。
我目前正在使用 asio 进行套接字 I/O,但我在 main()
中使用一个简单的循环来执行来自 stdin
的阻塞读取(在 stdin
和 Windows 上的朋友中无法进行异步 I/O)。我试图通过使用简单的发布/订阅机制来解耦一切。消息订阅者在自己的线程上运行,并通过std::condition_variable
接收来自std::deque
的消息。我目前有以下 I/O 相关的类:
asio::io_service
,绑定并监听端口,并接受远程连接。
SocketClient: 创建一个asio::io_service
,连接到远程主机并提供密码。
SocketConnection: 由 SocketServer 或 SocketClient 创建。验证远程主机(仅限服务器),从套接字读取,并在接收到SocketOutputMessage
时写入套接字(在单独的线程上)。
ConsoleWriter:在自己的线程上运行,在收到ConsoleOutputMessage
时写入stdout
。
对我来说,这种设计的主要缺陷是线程太多! SocketServer 需要自己的线程,因为阻塞了对asio::io_service::run()
的调用。同样,ConsoleWriter 在自己的线程上,以确保对stdout
的所有写入都发生在同一个线程上。我不想要另一个用于 SocketConnection 的线程,但由于 pub/sub 需要它。 (我希望消息总是在 receiver's 线程上发送)
请有人向我提供有关如何简化上述设计的建议(并希望减少所需的线程数)?为此,我应该了解哪些 asio 功能?如果可以,请提供一些示例代码和/或相关网站的链接或 SO 答案。
干杯。
【问题讨论】:
【参考方案1】:我建议您创建两个 io_services,第一个(您可以在线程中调度)应该用于所有套接字通信。)第二个(在主线程上调度)应该处理控制台活动。
例如:
main()
// Run the network service
std::thread service([]() net_service.run(); );
// Now run the console service
console_service.run();
要在两个服务之间进行通信,请使用io_service::post()
操作。这会将处理程序发布到在该上下文中执行的给定 io 服务上。例如。
client_socket.async_read_some(_data, [](...)
// Now you have the data and you want to print it..
console_service.post([=]() std::cout << "message " << _data << std::endl; );
);
所以现在当您从client_socket
中读取某些内容时,您将该数据发布到控制台服务 - 请注意,复制数据或者如果您想避免复制,请创建一些单独的队列,就像您拥有的那样,并且只需使用post()
机制通知其他io_service。
注意:另一个答案提到poll()
,这将导致上下文旋转,因为如果没有要完成的内容,它将立即返回。
【讨论】:
【参考方案2】:io_serivce.poll() 只是运行当前事件然后返回,我重复调用它而不是为此设置一个线程。
您可以通过某种形式的锁/互斥锁/自旋锁/...锁定写入部分来避免在其自己的线程上使用控制台编写器。
如果您使用 async_read() 而不是 read() ,您将提供一个回调,只要有要读取的数据就会被调用。这样你就可以让 asio 处理线程,你就不用担心了。
【讨论】:
以上是关于如何使用 asio 简化多线程设计?的主要内容,如果未能解决你的问题,请参考以下文章
Boost::asio - 如何中断阻塞的 tcp 服务器线程?