【问题标题】:PHP fork unexpected behaviour: Parent process waits till the child process exitsPHP fork 意外行为:父进程等待子进程退出
【发布时间】:2012-01-14 22:38:44
【问题描述】:

我有一个向用户显示 Web 表单的 php 脚本。当用户提交表单时,我将数据保存在数据库中,然后我创建一个子进程,向受新更改影响的数据库用户发送一些 SMS。

当我fork时,我检查了儿子,他正确发送了SMS,最后退出了。 但出于某种原因,父亲等待儿子完成他的任务。我不知道为什么会这样..

这是我的代码示例:

    // before this point, i've inserted some data in the database, and now i commit the transaction
    $conn->commit();
    $pid = pcntl_fork();
if ($pid == 0) {    // its the son, so he will send the messages
  $conn = new PDO('oci:dbname='.SERVER.';charset='.CHARSET, DATABASE, PASSWORD);
  $suppliersPhoneNumber = getSuppliersPhoneNumber($conn, ...);
  $conn = null;
  $sms = new MessageSender($suppliersPhoneNumber, $_POST['subCategory']);
  $sms->handleMessages();   // send the sms     
      //sleep(60);      
  exit(0);  // the son won't execute more code
}/

代码“sleep(60)”的那一行是我知道父亲在等孩子的方式。但是,如果儿子退出,这怎么可能?我知道父亲在等儿子,因为实际上我的脚本冻结了 1 分钟,等待时间。

我的想法是让父亲在数据库中插入所需的数据,最后他产生一个新的孩子来发送消息,但不等待他,所以我们可以向用户发送一个响应页面说一切都很好,而消息正在有效地发送。

这里出了什么问题?

提前考虑

编辑 问题没有解决,而是我按照下面注册的 Paulo H. 的解决方案。确实这是一个更好的方法。

【问题讨论】:

  • 这并不是您问题的真正答案,但是由于继承的资源,分叉附加到 Apache(或其他一些 Web 服务器)的 PHP 进程很复杂。就个人而言,我会在每次提交表单时创建一个数据库表并在其中插入一行。使用 CRON,您可以每分钟运行另一个 PHP 脚本来检查此表中的新条目并为每个新条目发送 SMS。对我来说,这是一种更简单的解耦后台逻辑的方法。

标签: php process fork


【解决方案1】:

我相信你是用Apache来运行这段代码的,我建议你完全运行separate process in the background

我认为问题正在发生,因为 Apache 在将信息发送到浏览器之前会等待这个子进程。无论如何,如果不在 php-cli 脚本中,不推荐 fork。

通常子进程保持僵尸模式,直到父进程调用“wait”,但似乎并非如此。

【讨论】:

  • 我按照你提供的链接,在那里他们通过 exec 调用了脚本。这将如何改变我在这里拥有的东西?是的,确实我正在使用 Apache 运行它。但这很奇怪,因为我也创建了一个分叉的示例。在该示例中,父代执行分叉,而子代只等待 60 秒,但父代将页面重定向到 google.com。奇怪的是,在这个例子中,父母没有等待儿子,当我通过浏览器加载我的脚本时,他立即重定向到谷歌。
  • 您必须创建另一个 PHP 脚本,然后调用 exec(sprintf("%s > %s 2>&1 & echo $!", "php yourscript.php", "/path/to/debug/file"),$childpid); `
  • 为什么使用 exec 会有什么不同?
  • Exec 会从头开始创建一个进程,而 fork 将通过从内存复制所有信息直到 fork 来创建一个新进程,包括资源处理程序和其他变量。这可以防止 Apache 因为分叉而挂起,在我看来是最好的解决方案。
  • 好的,我马上试试。但是每次我使用 exec 时,我都会在一个分叉的子进程中执行它,因为如果一切顺利,则在 exec 之后不会执行任何操作。我对吗?无论如何,我想在子调用 exec 到发送 SMS 的 php 脚本中进行分叉,对吗?
【解决方案2】:

通过派生一个进程,子进程将在同一个进程组中。也许 Apache 或您使用的任何 Web 服务器正在等待组中的所有进程结束,然后再分离和结束请求。

在分叉后尝试在孩子中调用 setsid() ,这应该将其作为新组的领导者。

http://www.php.net/manual/en/function.posix-setsid.php

【讨论】:

  • 嗯,你的解决方案奏效了!!但为什么?我在另一个脚本中有一个简单的例子,我派生了一个子进程,然后子进程等待 60 秒,父进程重定向到谷歌。如果我运行该脚本,则父母不会等待孩子退出。但是我在本主题中描述的脚本中并没有发生这种影响。在这种情况下我为什么要做 setid()?
  • 毕竟,这个解决方案没有奏效:p 当我将 setsid() 放入孩子时,父母不再等待孩子退出,但孩子由于某种原因没有执行。换句话说,我没有收到他应该发送的短信..
  • 你需要检查 php.ini 来找到日志文件的位置,它通常在 /var/log/php 之类的地方
  • 另外,检查setsid()的返回值。
  • 我的 php_ini 说:“error_log:没有价值”。好像没找到
【解决方案3】:

而不是

 exit();

试试这个

posix_kill(getmypid(),9);

请告诉我这是否有效。

【讨论】:

  • 不幸的是它没有用。我用你给我的指令替换了儿子的出口,但父母仍然等待儿子 60 秒,直到新页面返回到浏览器
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-05
  • 1970-01-01
  • 2019-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多