众所周知,当你分叉时,孩子会获得所有内容的副本,包括文件和网络描述符 - man fork
。
在PHP中,当您使用pcntl_fork时,会复制使用mysql_connect创建的所有连接,这有点问题 - php docs和SO question。在这种情况下常识说关闭父连接,创建新的并让孩子使用旧连接。但如果说父母需要创造许多孩子几秒钟呢?在这种情况下,您最终会创建加载的新连接 - 每一个叉子都有一个。
这在代码中意味着什么:
while (42) {
$db = mysql_connect($host, $user, $pass);
// do some stuff with $db
// ...
foreach ($jobs as $job) {
if (($pid = pcntl_fork()) == -1) {
continue;
} else if ($pid) {
continue;
}
fork_for_job($job);
}
mysql_close($db);
wait_children();
sleep(5);
}
function fork_for_job($job) {
// do something.
// does not use the global $db
// ...
exit(0);
}
好吧,我不想这样做 - 那就是与数据库的连接太多了。理想情况下,我希望能够实现与此类似的行为:
$db = mysql_connect($host, $user, $pass);
while (42) {
// do some stuff with $db
// ...
foreach ($jobs as $job) {
if (($pid = pcntl_fork()) == -1) {
continue;
} else if ($pid) {
continue;
}
fork_for_job($job);
}
wait_children();
sleep(5);
}
function fork_for_job($job) {
// do something
// does not use the global $db
// ...
exit(0);
}
你认为有可能吗?
其他一些事情:
答案 0 :(得分:2)
你唯一能做的就是让你的孩子等到彼此的孩子完成工作。这样您就可以使用相同的数据库连接(前提是没有任何同步问题)。但是当然你会有很多进程,这也不是很好(根据我的经验,PHP有很大的内存使用率)。如果有多个进程访问同一个数据库连接不是问题,您可以尝试创建共享连接的进程的“组”。所以你不必等到每个工作完成(你可以在整个团队完成后清理)并且你没有很多连接......
您应该问问自己是否真的需要为您的工作进程建立数据库连接。为什么不让父进程获取数据并将结果写入文件?
如果确实需要连接,则应考虑使用其他语言进行工作。 PHPs cli本身不是一个“典型的”用例(它是在4.3中添加的),而且多处理更像是一个黑客而不是支持的功能。
答案 1 :(得分:0)
如果孩子很快调用exec()或_exit(),你就没问题了。问题是如果孩子坚持并保留文件描述符的副本。
如果PHP有一个API,你也可以使用posix_spawn。这可能效果很好。
答案 2 :(得分:0)
我的建议(根据同一问题的个人经验)是在pcntl_fork()
之前关闭连接,然后根据需要在父进程和/或子进程中打开新连接。
如果您在父进程中打开新连接 ,则必须阻止SIGCHLD
信号(使用pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD)
)。儿童过程中不需要特别小心(除非他们也开始自己的孩子,以这种方式成为父母。)
SIGCHLD
是父进程在其中一个子进程完成时收到的信号。
在与服务器通信期间,MySQL客户端库使用nanosleep()
暂停程序的执行一段时间。 sleep()
函数会在时间过去时返回,但如果进程在挂起时收到信号,它们也会在时间之前返回。
当nanosleep()
由于信号而返回时(即在经过足够的时间之前),MySQL库会混淆并报告错误" MySQL服务器已经消失了#34;并且连接不能再使用了。这是一个误报,MySQL服务器仍然在那里等待查询,但客户端代码被错误时刻到达的信号所欺骗。
如果您有兴趣接收SIGCHLD
信号,那么您可以在运行MySQL查询之前阻止它,然后再次取消阻止它(以避免在与MySQL服务器通信期间收到它。
同时阅读this answer和this answer我写了类似的问题(它是相同的信息,但有更多细节和解释。)