我有一个带有自定义关闭处理程序的PHP命令行应用程序:
<?php
declare(ticks=1);
$shutdownHandler = function () {
echo 'Exiting';
exit();
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
curl_exec($ch);
curl_close($ch);
}
Run Code Online (Sandbox Code Playgroud)
如果我在进行CURL请求时使用Ctrl+ C终止脚本,则无效.命令只是挂起.如果我删除自定义关闭处理程序,Ctrl+ C立即杀死CURL请求.
为什么在定义SIGINT处理程序时CURL是不可杀死的?
真正有用的是给整个事物一些空间来处理它的信号处理魔法.这样的空间似乎是通过启用cURL的进度处理来提供的,同时还设置了"用户空间"进度回调:
while (true) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_NOPROGRESS, false); // "true" by default
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {
usleep(100);
});
curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
curl_exec($ch);
curl_close($ch);
}
Run Code Online (Sandbox Code Playgroud)
好像在进程回调函数中需要有"东西".空体似乎不起作用,因为它可能只是给PHP花费很多时间进行信号处理(硬核猜测).
把pcntl_signal_dispatch()回调似乎即使没有工作declare(ticks=1);的PHP 7.1.
...
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {
pcntl_signal_dispatch();
});
...
Run Code Online (Sandbox Code Playgroud)
即使使用空进程回调函数体pcntl_async_signals(true),也declare(ticks=1);可以使用代替工作.
这可能是我个人会使用的,所以我将把完整的代码放在这里:
<?php
pcntl_async_signals(true);
$shutdownHandler = function() {
die("Exiting\n");
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {});
curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
curl_exec($ch);
curl_close($ch);
}
Run Code Online (Sandbox Code Playgroud)
所有这三种解决方案使PHP 7.1以击球后几乎立即退出CTRL+C.
发送Ctrl + C命令时,PHP尝试在退出之前完成当前操作.
在最后看到最终的想法以获得更详细的解释
您的代码不会因为cURL未完成PHP而退出,因此在完成当前操作之前无法退出.
您为此练习选择的网站永远不会加载.
要解决,有一些并不加载,如更换URL https://google.com,例如
我编写了自己的代码示例,以准确显示何时/何时PHP决定退出:
<?php
declare(ticks=1);
$t = 1;
$shutdownHandler = function () {
exit("EXITING NOW\n");
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
print "$t\n";
$t++;
}
Run Code Online (Sandbox Code Playgroud)
在上图中,您可以看到当我通过Ctrl + C(由箭头显示)发出SIGINT命令时,它完成了它正在执行的操作,然后退出.
这意味着如果我的修复是正确的,那么在OP代码中杀死curl所需要的只是一个简单的URL改变:
<?php
declare(ticks=1);
$t = 1;
$shutdownHandler = function () {
exit("\nEXITING\n");
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
echo "CURL$t\n";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://google.com');
curl_exec($ch);
curl_close($ch);
}
Run Code Online (Sandbox Code Playgroud)
中提琴!正如预期的那样,脚本在当前进程完成后终止,就像它应该的那样.
您尝试卷曲的网站实际上是在无休止的旅行中发送您的代码.能够停止进程的唯一行动是CTRL + X,将最大执行时间设置,或CURLOPT_TIMEOUT卷曲的选择.CTRL+C当你拿出OUT时,原因pcntl_signal(SIGINT, $shutdownHandler);是因为PHP不再有内部函数正常关闭的负担.由于PHP不是并发的,当你确实有处理程序时,它必须在它执行之前等待它 - 它永远不会得到,因为cURL任务永远不会完成,从而留下永无止境的结果.
我希望这有帮助!