从cron

时间:2016-10-29 20:38:38

标签: php linux cron pthreads

好的,让我们慢慢开始......

我有一个运行并为我工作的pthreads脚本,当我通过ssh从命令行手动运行它时,100%的时间都经过测试和工作。脚本如下,主线程进程代码调整为模拟随机进程的运行时间。

class ProcessingPool extends Worker {
    public function run(){}
}
class LongRunningProcess extends Threaded implements Collectable {
    public function __construct($id,$data) {
        $this->id = $id;
        $this->data = $data;
    }

    public function run() {
        $data = $this->data;
        $this->garbage = true;

        $this->result = 'START TIME:'.time().PHP_EOL;

        // Here is our actual logic which will be handled within a single thread (obviously simulated here instead of the real functionality)
        sleep(rand(1,100));

        $this->result .= 'ID:'.$this->id.' RESULT: '.print_r($this->data,true).PHP_EOL;
        $this->result .= 'END TIME:'.time().PHP_EOL;

        $this->finished = time();
    }
    public function __destruct () {
        $Finished = 'EXITED WITHOUT FINISHING';
        if($this->finished > 0) {
            $Finished = 'FINISHED';
        }

        if ($this->id === null) {
            print_r("nullified thread $Finished!");
        } else {
            print_r("Thread w/ ID {$this->id} $Finished!");
        }
    }

    public function isGarbage() : bool { return $this->garbage; }

    public function getData() {
        return $this->data;
    }
    public function getResult() {
        return $this->result;
    }

    protected $id;
    protected $data;
    protected $result;
    private $garbage = false;
    private $finished = 0;
}

$LoopDelay = 500000; // microseconds
$MinimumRunTime = 300; // seconds (5 minutes)

// So we setup our pthreads pool which will hold our collection of threads
$pool = new Pool(4, ProcessingPool::class, []);

$Count = 0;

$StillCollecting = true;
$CountCollection = 0;
do {

    // Grab all items from the conversion_queue which have not been processed
    $result = $DB->prepare("SELECT * FROM `processing_queue` WHERE `processed` = 0 ORDER BY `queue_id` ASC");
    $result->execute();
    $rows = $result->fetchAll(PDO::FETCH_ASSOC);

    if(!empty($rows)) {

        // for each of the rows returned from the queue, and allow the workers to run and return
        foreach($rows as $id => $row) {
            $update = $DB->prepare("UPDATE `processing_queue` SET `processed` = 1 WHERE `queue_id` = ?");
            $update->execute([$row['queue_id']]);

            $pool->submit(new LongRunningProcess($row['fqueue_id'],$row));

            $Count++;
        }
    } else {
        // 0 Rows To Add To Pool From The Queue, Do Nothing...
    }


    // Before we allow the loop to move on to the next part, lets try and collect anything that finished
    $pool->collect(function ($Processed) use(&$CountCollection) {
        global $DB;

        $data = $Processed->getData();
        $result = $Processed->getResult();


        $update = $DB->prepare("UPDATE `processing_queue` SET `processed` = 2 WHERE `queue_id` = ?");
        $update->execute([$data['queue_id']]);

        $CountCollection++;

        return $Processed->isGarbage();
    });
    print_r('Collecting Loop...'.$CountCollection.'/'.$Count);


    // If we have collected the same total amount as we have processed then we can consider ourselves done collecting everything that has been added to the database during the time this script started and was running
    if($CountCollection == $Count) {
        $StillCollecting = false;
        print_r('Done Collecting Everything...');
    }

    // If we have not reached the full MinimumRunTime that this cron should run for, then lets continue to loop
    $EndTime = microtime(true);
    $TimeElapsed = ($EndTime - $StartTime);
    if(($TimeElapsed/($LoopDelay/1000000)) < ($MinimumRunTime/($LoopDelay/1000000))) {
        $StillCollecting = true;
        print_r('Ended To Early, Lets Force Another Loop...');
    }

    usleep($LoopDelay);

} while($StillCollecting);

$pool->shutdown();

因此,当 时,上述脚本将通过命令行运行 (已经调整为基本示例,并且在上面的示例中已经模拟了详细的处理代码) , 以下命令在每5分钟从cron 设置运行时会产生不同的结果......

/opt/php7zts/bin/php -q /home/account/cron-entry.php file=every-5-minutes/processing-queue.php

上述脚本在使用上述命令行调用时,会在脚本运行期间反复循环,并从数据库队列中收集任何新项,并将它们插入池中,这样就可以在运行和完成的时间,然后收集并在另一个循环发生之前更新队列,从数据库中提取任何新项目。此脚本将一直运行,直到我们在执行脚本期间处理和收集队列中的所有进程。如果脚本没有运行整整5分钟的预期时间,则循环被强制继续检查队列,如果脚本已经运行了5分钟标记,则它允许任何当前线程完成&amp;在结束前收集。请注意,上面的代码还包括基于代码的“flock”功能,该功能使得此空闲循环的未来crons在锁定解除后退出或启动,从而确保队列和线程不会相互碰撞。同样,所有这些工作都来自命令行通过SSH。

一旦我接受了上面的命令,并将其放入一个每5分钟运行一次的cron,基本上给了我一个永无止境的循环,同时保持记忆,我得到了不同的结果... < / EM>

该结果描述如下......脚本启动,检查flock,如果锁不存在则继续,它会创建锁,并运行上面的脚本。这些项是从DB中的队列中获取的,并插入池中,池按预期一次触发4个线程。但是 意外的结果是run()命令没有好像被执行了,而__destruct函数运行 ,还有一个“线程w / ID 2完成!”消息类型返回到输出。这反过来意味着事物的收集方不收集任何东西,并且启动脚本(cron脚本本身 /home/account/cron-entry.php文件=每5分钟/处理队列.php )将所有内容放入池中并完成破坏后完成。它过早地“完成”了cron作业,因为除了循环并从队列中提取任何新内容之外别无他法,因为在队列中处理== 1时它们被视为“正在处理”。

然后问题终于变成了...... 如何让cron的脚本知道生成并运行它们的线程(),而不关闭池,然后才能执行任何操作?

(注意......如果您复制/粘贴提供的脚本,请注意我在删除详细逻辑后没有测试它,所以可能需要一些简单的修复...请不要挑选说代码,因为这里的关键是如果脚本是从命令行执行的话pthreads有效,但是当从CRON执行脚本时无法正常运行。如果你计划用非建设性的批评评论,请用你的手指做其他事情!)

Joe Watkins!我需要你的才华!在此先感谢!

1 个答案:

答案 0 :(得分:1)

毕竟,问题似乎与用户权限有关。我在cpanel中设置了这个特定的cron,当手动运行命令时,我以root身份登录。

在root crontab中设置此命令后,我能够让它成功运行池中的线程。我现在唯一的问题是有些线程永远不会完成,有时我无法关闭池。但这是一个不同的问题,所以我将在其他地方打开另一个问题。

对于遇到此问题的人,请确保知道cron的所有者与php的pthreads有关。