【问题标题】:Sleep doesn't wait睡眠不等待
【发布时间】:2015-08-05 16:06:39
【问题描述】:

我的脚本检查 HTTP 服务器是否响应...它应该每 60 秒询问列表中的所有服务器。但它根本不等。知道为什么吗?它确实执行睡眠线(当我在那里做一些回声时,它确实出现了),但在这 60 秒内不会停止......只有在达到子限制时才会停止(在这种情况下 - 从不)。知道为什么吗?

PHP 5.4.41-0+deb7u1

<?php

    echo "[".date("Y-m-d H:i:s")."] Starting...\n";

    declare(ticks = 1); // cpu directive

    error_reporting(0); // screw warnings
    set_time_limit(0); // no limits!

    $ctx = stream_context_create(array( // timeout file_get_contents context
        'http' => array(
            'timeout' => 30 // half of time between requests
            )
        )
    );

    $test_urls = array( // test urls
        'URL1' => 'http://www.google.com',
        'URL2' => 'http://www.ebay.de'
    );

    $max=10;
    $child=0;
    $rec_id=0;

    function sig_handler($signo) {
        global $child;
        switch ($signo) {
                case SIGCHLD:
                $child -= 1;
        }
    }

    pcntl_signal(SIGCHLD, "sig_handler"); // register fork signal handler to count running children

    $loop = 0;

    while (true){ // <main_loop>
    $loop++;

        foreach($test_urls as $uid => $url){ // <test_urls>

        echo "[".date("Y-m-d H:i:s")."] ENTERING LOOP:$loop\n";

            while ($child >= $max) {
                        sleep(1);
                    }

            $child++;
            $rec_id++;

            if($rec_id > 999999) $rec_id = 1; // limit rec_id, so it wouldn't cause an out of bounds error

            $pid=pcntl_fork();

            if($pid){

                // If you're here, something is totally messed up.

            }else{ // <fork>

                $req_start = microtime(true);
                $result = file_get_contents($url,NULL,$ctx);                
                $req_time = microtime(true) - $req_start;

                if($result) echo "[".date("Y-m-d H:i:s")."] OK - $req_time\n";
                else echo "[".date("Y-m-d H:i:s")."] ERROR - $req_time\n";

                sleep(1); // avoid segmentation fault, when fork ends before handling signals
                exit;

            } // </fork>

        } // </test_urls>

    echo "[".date("Y-m-d H:i:s")."] GOING TO SLEEP ON LOOP: $loop\n";
        sleep(60); // test every 60 seconds (if childs are available!)
    echo "[".date("Y-m-d H:i:s")."] WAKING UP LOOP: $loop\n";

    } // </main_loop>

    while($child != 0){
        sleep(1);
    }
?>

结果:*

[2015-08-05 18:17:43] Starting...
[2015-08-05 18:17:43] ENTERING LOOP:1
[2015-08-05 18:17:43] ENTERING LOOP:1
[2015-08-05 18:17:43] GOING TO SLEEP ON LOOP: 1
[2015-08-05 18:17:43] OK - 0.36189103126526
[2015-08-05 18:17:44] OK - 0.99003601074219
[2015-08-05 18:17:44] WAKING UP LOOP: 1
[2015-08-05 18:17:44] ENTERING LOOP:2
[2015-08-05 18:17:44] ENTERING LOOP:2
[2015-08-05 18:17:44] GOING TO SLEEP ON LOOP: 2
[2015-08-05 18:17:44] OK - 0.30263018608093
[2015-08-05 18:17:45] WAKING UP LOOP: 2
[2015-08-05 18:17:45] ENTERING LOOP:3
[2015-08-05 18:17:45] ENTERING LOOP:3
[2015-08-05 18:17:45] GOING TO SLEEP ON LOOP: 3
[2015-08-05 18:17:45] OK - 0.445955991745
[2015-08-05 18:17:45] OK - 1.2020018100739
[2015-08-05 18:17:45] WAKING UP LOOP: 3
[2015-08-05 18:17:45] ENTERING LOOP:4
[2015-08-05 18:17:45] ENTERING LOOP:4
[2015-08-05 18:17:45] GOING TO SLEEP ON LOOP: 4
[2015-08-05 18:17:46] OK - 1.0700960159302
[2015-08-05 18:17:46] OK - 0.5119640827179
[2015-08-05 18:17:46] WAKING UP LOOP: 4
[2015-08-05 18:17:46] ENTERING LOOP:5
[2015-08-05 18:17:46] ENTERING LOOP:5
[2015-08-05 18:17:46] GOING TO SLEEP ON LOOP: 5
[2015-08-05 18:17:46] WAKING UP LOOP: 5
[2015-08-05 18:17:46] ENTERING LOOP:6
[2015-08-05 18:17:46] ENTERING LOOP:6
[2015-08-05 18:17:46] GOING TO SLEEP ON LOOP: 6
[2015-08-05 18:17:47] OK - 0.43205118179321
[2015-08-05 18:17:47] WAKING UP LOOP: 6
[2015-08-05 18:17:47] ENTERING LOOP:7
[2015-08-05 18:17:47] ENTERING LOOP:7
[2015-08-05 18:17:47] OK - 1.3988509178162
[2015-08-05 18:17:47] GOING TO SLEEP ON LOOP: 7
[2015-08-05 18:17:47] OK - 0.59426283836365
[2015-08-05 18:17:47] WAKING UP LOOP: 7
[2015-08-05 18:17:47] ENTERING LOOP:8
[2015-08-05 18:17:47] ENTERING LOOP:8
[2015-08-05 18:17:47] GOING TO SLEEP ON LOOP: 8
[2015-08-05 18:17:47] OK - 1.2100639343262
[2015-08-05 18:17:47] OK - 0.5920991897583

【问题讨论】:

  • 不会exit; 一起杀死脚本执行,因此它永远不会达到第二次迭代......?
  • 不,exit退出fork。它确实永远执行循环,但无需等待......当我给出“echo 'something';”时直接在 sleep(60) 之后和之前,它每次都会回显。不退出分叉会导致分段错误。
  • @FlashThunder 我 90% 确定它会退出 foreach .. 如果不是育儿(并且有问题)while(true) 尝试 continue
  • 编辑证明你错了。请立即检查。

标签: php


【解决方案1】:

sleep 可以被信号中断,在这种情况下它将返回剩余的秒数并且不会继续。它可能收到的信号之一是当孩子在执行期间被杀死时 (SIGCHLD)。您可以检查sleep 的返回值以确定是否有信号中断它,但我猜它确实在SIGCHLD 上退出。

在 linux 机器上返回值 will be the remaining seconds to sleep,因此您可以将睡眠置于循环中并睡眠直到剩余秒数为 0,或者如果出现其他类型的错误(由 FALSE 响应发出信号)

这样的事情可能会奏效:

$timeLeft = 60;
while ($timeLeft > 0) {
   $timeLeft = sleep($timeLeft);
}

另外请注意,在您的原始代码中,您有一条注释说明:

// If you're here, something is totally f***ed up.   *<-- Edited for family friendliness*

这实际上是分叉完成后的父部分,因此该分支实际上每次都会执行。您可能想要更改此注释,因为它可能会让您或任何其他阅读代码的人感到困惑,或者通过使用if (!$pid) { 检查来完全删除它

【讨论】:

  • 有道理...有什么办法可以避免这种情况?
  • 那行得通,甚至没有错过秒...酷,thanx...不算数,这不是PHP语法,但没关系。谢谢。
  • 这是有道理的.. 从来没有猜到过.. 但它不是真的违背了sleep 的目的吗?
  • @Pogrindis:实际上sleep 在许多其他编程语言中也是如此,including C,可能所有其他实现都从中复制。例如,在 Java 中,您需要处理睡眠 will throw exceptions,以防它收到信号(因此更明显的是它可能不会一直处于睡眠状态)
  • 我完全没有怀疑它会在sigchld 上打破睡眠。至少它在 PHP 中整整一秒后打破了它,这就是答案正常工作的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-03
  • 1970-01-01
  • 2012-10-29
  • 1970-01-01
  • 2017-07-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多