如何在不阻塞线程的情况下调用 PHP


How to call a PHP without blocking the thread?

我有2个PHP文件。一个文件是调用方.php另一个是辅助角色.php

调用者.php将在(Linux)系统上启动worker.php并且调用者.php应立即结束(而worker.php仍在服务器上工作)

工作线程.php花费大量时间,它会将状态写入数据库或文件。

我希望能够在启动"php worker.php"的浏览器中打开调用者.php,关闭浏览器,5 分钟后返回并检查状态。(或者脚本将在完成后发送邮件) - 任何想法如何做到这一点?

您可以结束客户端连接并继续处理,全部完成。如果处理花费的时间超过php/httpd全局配置允许的时间,您可能还希望增加php超时。请参阅set_time_limit();

这不是您所要求的,但我认为这可能是 X/Y 问题,因此在这里我们使用可用于以下方面的技术:

  • 向用户显示处理已开始页面。
  • 关闭用户连接。
  • 开始更长的处理时间。

所以基本上这回答了你描述的需求,而不是"如何做X?

访客

// Ignore user abort and set Connection close header
ignore_user_abort(true);
header("Connection: close");
// Start controlled buffering
ob_start();
echo "<h1>Processing started, come back tomorrow to see results</h1>";
// Get buffer length and manually add it to headers.
$size = ob_get_length();
header("Content-Length: $size");
// Flush buffers, user sees "Processing started" message.
ob_end_flush();
flush();
// At this point connection to client should be closed already.
// Here we start our worker, there is no need to do fork() or 
// anything like that. Client browser is not listening anymore.
// All we need to do is simply start working and start writing
// status logs to somewhere so that client can retrieve status
// with another request, let's say from checkworkerstatus.php
include "worker.php";
$worker = new worker();
$worker->start_long_processing();

不分叉或杀死caller,让worker独自奔跑?

没错,那是因为没有必要。

的,您要求的行为是"调用方.php将在(linux)系统上启动worker.php并且调用者.php应该立即结束(当worker.php仍在服务器上工作时)"。但是,如果您不想使用 php 进行多线程单用户应用程序,则无需这样做。只需让用户 1. 开始某事,2. 让它运行,3. 断开用户连接,4. 返回开始,用户可以开始更多事情,或者如果已经足够无聊就退出。

当然,您可以将flush();后的所有内容替换为任何内容,不同之处在于客户端不再侦听,因此所有输出都进入名为/dev/null 的黑洞。例如,用这样的东西来测试它:

...end_flush();
flush();
// At this point connection to client should be closed already.
echo "Nobody sees this message";
while(rand(0,10) != 1) {
    echo "Nobody sees this message";
    error_log(date("mdyHis")." Still running...");
    sleep(10);
}
...

有关详细信息,请参阅 http://php.net/features.connection-handling。
另请阅读 http://php.net/function.flush 刷新缓冲区和关闭用户连接是否有任何问题。
还有一个问题(有答案)关于为什么PHP不支持多线程?

  1. 调用方运行辅助角色
  2. worker 在/tmp/中创建 FIFO 文件并向其写入有关进度的信息(您也可以使用套接字)
  3. 调用方读取此文件并显示用户实际进度

如果你将Apache/nginx配置得很好,它将允许在同一时间执行两个PHP文件(就像我的一样)。

您应该阅读有关posix_mkfifo:http://php.net/manual/en/function.posix-mkfifo.php 的信息(也有用法示例)。

这可能有点

强,但这是最快和直接的方法:

  • 调用方使用 >
  • IFRAME 的 SRC 是工作线程(此处调用者将参数作为 GET 参数传递给工作线程)
  • 工作线程的第一行在下面的代码中

法典:

ignore_user_abort(true);
set_time_limit(0);

当您关闭浏览器工作人员(应该)计数工作时!

见 http://www.php.net/manual/en/function.ignore-user-abort.php