如何使用 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 相关的类:

SocketServer:在自己的线程上运行,创建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 服务器线程?

boost::asio::socket 线程安全

课二:前摄器设计模式(不使用多线程并发)

Boost::ASIO - 如何专用 2 个线程来处理接收和发送消息

多线程和Boost::Asio

设计一个 C++ asio 流服务器