C++ 使用fork()和boost::asio的进程池
我目前正在尝试实现一个可以由父进程与之通信的进程池。在父进程通知子进程之前(可能使用信号),子进程不应退出。现在,我脑海中出现了几个问题,我很高兴能通过我的MWE获得一些信息:C++ 使用fork()和boost::asio的进程池,c++,asynchronous,process,fork,boost-asio,C++,Asynchronous,Process,Fork,Boost Asio,我目前正在尝试实现一个可以由父进程与之通信的进程池。在父进程通知子进程之前(可能使用信号),子进程不应退出。现在,我脑海中出现了几个问题,我很高兴能通过我的MWE获得一些信息: #include <iostream> #include <boost/thread.hpp> #include <boost/process/async_pipe.hpp> #include <boost/asio.hpp> #include <boost/arr
#include <iostream>
#include <boost/thread.hpp>
#include <boost/process/async_pipe.hpp>
#include <boost/asio.hpp>
#include <boost/array.hpp>
static const std::size_t process_count = 3;
static void start_reading(boost::process::async_pipe& ap)
{
static boost::array<char, 256> buf;
ap.async_read_some(boost::asio::buffer(buf.data(), buf.size()), [](const boost::system::error_code& error, std::size_t bytes_transferred)
{
if(!error)
{
std::cout << "received " << bytes_transferred << " from pid " << getpid() << " " << buf[0] << "...." << std::endl;
// perform some heavy computations here..
}
});
}
static void start_writing(boost::process::async_pipe& ap)
{
boost::array<char, 256> buf;
buf.fill('A');
ap.async_write_some(boost::asio::buffer(buf.data(), buf.size()), [&ap](const boost::system::error_code& error, std::size_t bytes_transferred)
{
if(!error)
{
std::cout << "parent " << getpid() << " sent " << bytes_transferred << " to [" << ap.native_source() << "," << ap.native_sink() << "]" << std::endl;
}
});
}
int main()
{
try
{
boost::asio::io_service io_context;
// prevent the associated executor from stopping
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard = boost::asio::make_work_guard(io_context);
pid_t main_process = getpid();
std::cout << "before forks " << main_process << std::endl;
std::vector<boost::process::async_pipe> pipes;
pipes.reserve(process_count);
for(std::size_t i = 0; i < process_count; i++)
{
pipes.emplace_back(io_context);
io_context.notify_fork(boost::asio::io_service::fork_prepare);
pid_t pid = fork();
if(pid == 0)
{
io_context.notify_fork(boost::asio::io_service::fork_child);
// perform some costly initialization here...
boost::process::async_pipe& ap = pipes[i];
std::cout << "child " << getpid() << " listening to [" << ap.native_source() << "," << ap.native_sink() << "]" << std::endl;
start_reading(ap);
io_context.run();
}
else if(pid > 0)
{
io_context.notify_fork(boost::asio::io_service::fork_parent);
}
else
{
std::cerr << "fork() failed" << std::endl;
}
}
// only parent gets there
start_writing(pipes[0]);
start_writing(pipes[0]);
start_writing(pipes[1]);
start_writing(pipes[2]);
io_context.run();
}
catch(const std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 1;
}
我当时主要关心的是如何无限地读取工作进程(child)中的数据,只要该进程还不忙。一旦worker从async_read_some进入处理程序,它就会执行注释中所述的一些计算(可能需要几秒钟)。在执行此操作时,进程应该也将被阻止,之后我想通知我的家长再次准备好并通过管道接受新的读取。到目前为止,我还不知道该怎么做。从子进程通知父进程本身并不是必需的,但是父进程需要随时跟踪所有空闲的子进程,以便它可以通过相应的管道发送新的输入
除此之外,还有一件事我还没有得到:
请注意,boost::array buf代码>在start\u reading
中是static
。如果我删除了静态修饰符,我就永远不会进入async\u read\u some
的完成处理程序,这是为什么
编辑:
在完成例程中再次调用start\u reading
,将继续读取。但是,没有父进程“知道”它
编辑2:
直到现在,我才想出了一个可行的方法(我想有几种方法)。我还没有完成实现,但共享互斥体的工作与预期一样。下面是一些伪代码:
process_pool
{
worker get_next_worker()
ScopedLock(memory_mapped_mutex);
free_worker = *available.rbegin()
available.pop_back()
return free_worker;
memory_mapped_vec<worker> available;
};
server::completion_handler_async_connect()
get_next_worker().socket().write(request)
worker::completion_handler_async_read()
// do something very long before locking
ScopedLock(memory_mapped_mutex);
process_pool::available.push_back(self);
process\u池
{
工人得到下一个工人()
ScopedLock(内存映射互斥);
free_worker=*可用。rbegin()
可用。pop_back()
免费返回工作人员;
内存映射向量可用;
};
服务器::完成\u处理程序\u异步\u连接()
获取下一个工作者().socket().write(请求)
worker::完成\u处理程序\u异步\u读取()
//在锁定前很久做一些事情
ScopedLock(内存映射互斥);
进程池::可用。推回(自身);
除此之外,还有一件事我还没有得到:注意boost::array buf;在开始读取时是静态的。如果我删除了静态修饰符,我就永远不会进入async\u read\u some的完成处理程序,这是为什么
这是因为buf
是一个本地变量,它在start\u reading
退出后不存在。但是,async\u read
(或任何其他async\u XXXX
调用)会立即返回,而无需等待操作完成。因此,如果缓冲区没有持续存在,那么您正在将对未指定堆栈空间的悬空引用写入到缓冲区中,从而导致
至于来回通信,这在进程之间是不必要的复杂。有什么原因不能使用多线程吗?这样,所有工作人员都可以简单地监视共享队列
当然,您可以使用进程之间共享的队列来设置相同的队列(在这种情况下,我建议不要使用Asio通过管道执行,而是使用。感谢您首先指出了buf的问题,直到现在我仍然无法解决它。就线程而言,我确实需要使用进程,因为执行的请求可能会崩溃,服务器在这种情况下不能终止。最终这将导致创建一个tcp/ip服务器,并为每个接受计划一个预分叉()进程。我正在使用“真实”服务器中已经存在的io_服务对象,这会干扰消息队列吗?我到目前为止还没有看过它。我自己也有一个想法,并将更新我的帖子。你觉得这个怎么样?
process_pool
{
worker get_next_worker()
ScopedLock(memory_mapped_mutex);
free_worker = *available.rbegin()
available.pop_back()
return free_worker;
memory_mapped_vec<worker> available;
};
server::completion_handler_async_connect()
get_next_worker().socket().write(request)
worker::completion_handler_async_read()
// do something very long before locking
ScopedLock(memory_mapped_mutex);
process_pool::available.push_back(self);