非常简短的总结:
我正在使用boost :: asio实现一个简单的TCP服务器,该服务器允许非阻塞地接受新连接。在处理新连接的逻辑内,将执行大量工作,这可能需要几分钟的时间。 特别是,我将开始一个新进程,并在读取stdin,stderr及其返回代码的同时等待其完成。我只想为每个连接启动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 ++的工作线程,并等待其完成。但是服务器可以同时接受新的连接并开始新的编译。
连接的运行方法看起来与此错误处理类似
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:吗?
实际上是否有任何方法可以执行此操作,而无需为每个连接使用辅助线程或进程?最后,我需要在主线程中等待结果,以阻止接受新连接。我有点接触了协程,但是仍然不了解它的细节,并且在这种情况下也不认为会对我有帮助。
非常感谢您!
答案 0 :(得分:1)
您尝试过此解决方案吗?
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
可能阻止执行,那么您可以将它们进一步变为异步而不是并发阻止(因为其他挂起的处理程序可能会饿死)
答案 1 :(得分:0)
我现在也确实在提到的示例之前实现了#2,它似乎可以满足我的需求。我仍然会对某些输入非常感兴趣,尤其是考虑到#4。