c++ - boost::asio 允许在连接处理程序阻塞时非阻塞接受新连接
问题描述
非常简短的总结:
我正在使用 boost::asio 实现一个简单的 TCP 服务器,它允许非阻塞地接受新的连接。在处理新连接的逻辑中,将执行许多可能需要几分钟的工作。特别是我将启动一个新进程并等待它完成,同时读取标准输入、标准错误和它的返回码。只是图像我想为每个连接启动 g++,编译几个源文件并获取构建信息。虽然 g++ 在单独的进程中运行,但我仍然希望能够接受新的连接。
我是 boost::asio 的新手,正在寻找一些设计建议和对我当前想法的输入。
选项#1:为每个连接使用一个线程并将其分离
#include <boost/asio.hpp>
#include "tcp_connection.hpp"
#include <thread>
#include <functional>
using boost::asio::ip::tcp;
class tcp_server
{
public:
tcp_server(boost::asio::io_context& io_context, unsigned short port_num)
: m_io_context(io_context),
m_acceptor(io_context, tcp::endpoint(tcp::v4(), port_num)),
m_port_num(port_num)
{
// create initial connection that will be accepted
create_connection();
}
private:
void create_connection()
{
// create new connection that will be accepted
tcp_connection::pointer new_connection = tcp_connection::create(m_io_context);
// can't mix std::bind with boost::asio::placeholders ...
m_acceptor.async_accept(new_connection->socket(),
boost::bind(&tcp_server::handle_accept, this,
boost::asio::placeholders::error));
// save new connection to be handled next
m_curr_connection = new_connection;
}
void handle_accept(const boost::system::error_code& error)
{
if(!error)
{
// run new connection in own thread
std::thread t(std::bind(&tcp_connection::run, m_curr_connection));
// create next connection that will be accepted
create_connection();
// detach thread before it goes out of scope
t.detach();
}
}
boost::asio::io_context& m_io_context;
tcp::acceptor m_acceptor;
tcp_connection::pointer m_curr_connection;
unsigned short m_port_num;
};
所以连接的接受都是在主线程中使用 async_accept 异步执行的。对于处理,我正在创建一个调用 g++ 并等待它完成的工作线程。然而,服务器可以同时接受新的连接并开始新的编译。
连接的 run 方法与我们的错误处理类似
auto prog = boost::process::search_path("g++");
boost::asio::io_context io_context;
std::future<std::string> data;
boost::process::child processCobol(prog, "main.cpp"
boost::process::std_in.close(),
boost::process::std_out > boost::process::null,
boost::process::std_err > data,
io_context);
io_context.run();
m_message = data.get();
我在这里也使用异步 I/O,但是,此时同步读取结果对我来说也足够了。
选项 #2:使用分叉方法
假设我有一个libg++
可以直接链接到服务器的。我可以为每个连接使用“经典”分叉方法,如下所示:https ://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/example/fork/process_per_connection.cpp并放置一个g++_compile()
在叉子后直接呼叫。我仍然可以接受新连接,因为每个连接都有一个单独的过程。
选项#3:使用 boost::process::spawn 并通过共享内存读回结果,例如使用 boost::interprocess
我可以生成一个新进程,将其分离,并在某个时候通过共享内存读回结果,而不是创建子进程。有些东西告诉我这不是一个好主意 D:
选项#4:?
实际上有什么方法可以做到这一点,而无需为每个连接使用辅助线程或进程?最后在某个时候我需要在我的主线程中等待结果,阻止接受新连接。我有点接触协程,但仍然缺乏了解它的细节,并且认为它在这种情况下也不会帮助我。
非常感谢!
解决方案
你试过这个解决方案吗?
1) 创建一个 asio::thread_group 并传递一个F
调用io_service::run
.
asio::thread_group tg{ [](){io_service.run()}, x }; \\ x is the number of threads you want
2)在你的handle_accept
,而不是做std::thread t(std::bind(&tcp_connection::run, m_curr_connection));
和 t.detach()
做post(io_service, your_work) where your_work can be any callback or functionObject
。
自由站立asio::post
将放入your_work
,io_service::queue (briefly)
从那里它可能会同时执行。
注意:如果your_work
可能在执行中阻塞,那么你可以进一步将它们变成异步而不是并发阻塞'(因为其他挂起的处理程序可能会饿死)
推荐阅读
- sql - 嵌套 JSONB 字段的 Postgres 查询
- javascript - 我如何为我的画廊构建这个概念?
- javascript - 尝试创建具有多个起点的 HTML5 视频序列
- python - TimeoutException:消息:Selenium Python
- jmeter - JMeter Recording 工作正常,但浏览器未加载浏览器上的所有控件
- node.js - Heroku/React 部署 - 无法从 @heroku-cli/plugin-buildpacks 访问随机依赖项
- javascript - 无法在带有来自 node.js 的 src 的视频标签上使用时间线
- c - Flex/Bison 程序给出语法错误消息
- sql - 用同一组中的下一个值填充值
- r - 使用 st_read() 导入多个 GPX 文件?