无$dbh访问权限时,如何防止fork子进程断开DBD::Pg连接?
解决fork子进程导致DBD::Pg父进程连接终止的问题
我有一个需要通过fork()执行即发即弃(fire-and-forget)代码的库。但fork生成的子进程退出时,DBD::Pg的END块会被调用,意外终止父进程的数据库连接。此外,子进程代码无法访问$dbh,也不能use DBI。
重现问题的代码
use strict; use warnings; use DBI; my $dbh = DBI->connect('dbi:Pg:dbname=foo', '', ''); if (!fork()) { CORE::say 'In child, doing stuff!'; exit 0; } sleep 2; # 留足够时间让子进程被回收 my $sth = $dbh->prepare(q[select NOW();]); $sth->execute(); # 报错!连接被终止! my $time = $sth->fetch->[0]; CORE::say "It is $time o'clock";
报错信息
DBD::Pg::st execute failed: server closed the connection unexpectedly
This probably means the server terminated abnormally
解决方案
1. 子进程中清除DBD::Pg的END钩子
子进程退出时触发的DBD::Pg的END块会关闭共享的数据库连接,在子进程中移除该钩子即可避免问题,不需要use DBI:
if (!fork()) { # 移除DBD::Pg的END钩子 if (exists $INC{'DBD/Pg.pm'}) { no strict 'refs'; @{"DBD::Pg::END"} = (); } CORE::say 'In child, doing stuff!'; exit 0; }
2. 使用POSIX::_exit替代exit
exit会触发所有END块和对象析构函数,而POSIX::_exit直接终止进程,不执行任何清理逻辑,是即发即弃场景的最简方案:
use POSIX; if (!fork()) { CORE::say 'In child, doing stuff!'; _exit(0); # 直接终止进程,不触发END块 }
3. 清空子进程的END队列
如果不需要子进程执行任何模块的END逻辑,可以直接清空整个@END数组,彻底避免清理操作:
if (!fork()) { @END = (); # 清空所有模块的END钩子 CORE::say 'In child, doing stuff!'; exit 0; }
内容的提问来源于stack exchange,提问作者Rawley Fowler
相关产品推荐
相关产品推荐

