PHP-多线程和池

时间:2019-01-16 12:43:18

标签: php php-pthread

我正在PHP Pool中使用pthread对象,并制作了以下测试脚本,以查看池应如何工作。我坚信,池化应该做的是,获得给定数量的任务,最多开放x个工作人员,并为他们分配任务,并在工作人员完成任务后立即提供(如果更多)任务可用,为该工作人员分配新任务。

给出以下示例和上述假设:

class Work extends Threaded {
    public $id;

    public function __construct($id) {
        $this->id = $id;
    }

    public function run() {
        if ($this->id == 0) {
            sleep(3);
            echo $this->id . " is ready\n";
            return;
        } else {
            echo $this->id . " is ready\n";
            return;
        }
    }
}

$pool = new Pool(2, 'Worker', []);
for ($i=0; $i<4; $i++) $pool->submit(new Work($i));
while ($pool->collect());
$pool->shutdown();

我希望此脚本输出以下信息:

  

1已准备好
  2准备好了
  3准备好了
  0准备就绪

因为实际上有2个工作人员可用,并且由于第一个工作人员偶然遇到的sleep操作,所以任务1,2,3必须由第二个工作人员完成。

相反,我得到的输出是:

  

1已准备好
  3准备好了
  0已经准备好了
  2准备好了

很明显,工人1被分配了作业0,一开始就分配了作业2,因此工人2在完成作业1和3之后只是等待,而不是从工人1接管作业2。 >

这是一个错误吗?还是打算以此方式工作?

我的PHP版本:

PHP 7.2.14 (cli) (built: Jan  9 2019 22:23:26) ( ZTS MSVC15 (Visual C++ 2017) x64 )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

3 个答案:

答案 0 :(得分:0)

由于某种原因,由于我已经将Windows更新到1809,所以Docker已经无法使用了,因此发布未经测试。 (很抱歉,没有输出可提供给atm)


修改后的现有代码,我在一个带有计数器+睡眠的项目中使用。

$pool = new Pool(2);
foreach ([0,1,2,3] as $count) {
    $pool->submit(
        new class ($count) extends Threaded
        {
            private $count;

            public function __construct(int $count)
            {
                $this->count= $count;
            }

            public function run()
            {
                if ($this->count== 0) {
                    sleep(3);
                    echo $this->count . " is ready\n";
                } else {
                    echo $this->count . " is ready\n";
                }
            }
        }
    );
}

while ($pool->collect());

$pool->shutdown();

我使用匿名类(new class ($count) extends Threaded)作为submit()参数。

在服务器上,使用在Alpine 3.8上运行PHP ZTS 7.2.13的Docker实例,可以完美地运行

答案 1 :(得分:0)

我回答:根据我对php中pthreads的了解,池就像可以同时运行的处理php.exe的数量。

因此,在您的情况下,您可以使用use yii\bootstrap\ActiveForm;

定义两个池

因此,让我们对其进行抽象说明。有2个池,分别称为new Pool(2, 'Worker', []);PoolA

从0循环到3,每个循环将任务提交到Pool。

从0到3,共有4个任务,可以通过PoolBtask0task1task2来调用它们。

从我的角度来看,当发生循环时,应该是这样的队列

task3

但是从PoolA -> submit task0 PoolB -> submit task1 PoolA -> submit task2 PoolB -> submit task3 到任务0,直到任务3。

  

情况/条件

当参数(在本例中为构造函数的$ id)为0,然后为class Work时,您可以在run()=>中定义一些逻辑。

在这种情况下,sleep(3)是包含参数($ id)的提交PoolA的值为0,task0将等待3秒钟。 PoolA还要提交PoolA

另一方面,从这种情况下,task2提交PoolBtask1不需要等待3秒钟。

因此,当task3运行时,最有可能发生的可能队列

while($pool->collect());

所以我认为输出为

是正确的
  

1已准备好
  3准备好了
  0准备好了
  2准备好了

有一个问题。

  

为什么只有PoolA延迟了,即使PoolA延迟了task2没有提交给PoolB还是task1或task3没有提交给PoolA的原因?

嗯,我也不明白。我的任务类似于您的任务,经过多次实验,我不确定使用task1 (PoolB) task3 (PoolB) task0 (PoolA) ->>>> PoolA delayed because from task0 needs to sleep for 3 seconds task2 (PoolA) 的{​​{3}}是Pool & Threaded

答案 2 :(得分:0)

单个线程的回声可能是骗人的。 我经常发现他们似乎在被调用之前就已经执行了。我建议避免从线程内部回显,除非您不关心顺序,因为测试特定情况等仍然很有用。

下面是一些代码,可以解决代码执行时间的任何问题,因为此代码按实际执行时间对结果进行排序。 (这也是如何从线程池中取回结果的一个很好的例子。)

<?php
class Work extends Threaded {
    public $id;
    public $data;
    private $complete = false;
    public function __construct($id) {
        $this->id = $id;
    }

    public function run() {
        $temp = array();
        if ($this->id == 0) {
            echo "<pre>".$this->id . " started (from inside threaded)";
            $temp[] = array(microtime(true), $this->id . " started");
            sleep(3);
        }
        echo "<pre>".$this->id . " is ready (from inside threaded)";
        $temp[] = array(microtime(true), $this->id . " is ready");
        $this->data = (array) $temp; // note: it's important to cast as array, otherwise you will get a volitile
        $this->complete = true;
    }

    public function isDone() {
        return $this->complete;
    }
}

// we create a custom pool, to pass on our results
class ExamplePool extends Pool {
    public $dataAr = array(); // used to return data after we're done
    private $numTasks = 0; // counter used to know when we're done
    private $numCompleted = 0; // keep track of how many threads finished
    /**
     * override the submit function from the parent
     * to keep track of our jobs
     */
    public function submit(Threaded $task) {
        $this->numTasks++;
        parent::submit($task);
    }
    /**
     * used to wait until all workers are done
     */
    public function process() {
        // Run this loop as long as we have
        // jobs in the pool
        while ($this->numCompleted < $this->numTasks) {
            $this->collect(function (Work $task) {
                // If a task was marked as done, collect its results
                if ($task->isDone()) {
                    //this is how you get your completed data back out [accessed by $pool->process()]
                    $this->dataAr = array_merge($this->dataAr, $task->data);
                    $this->numCompleted++;
                }
                return $task->isDone();
            });
        }
        // All jobs are done
        // we can shutdown the pool
        $this->shutdown();
        return $this->dataAr;
    }
}

$pool = new ExamplePool(4);
for($i=0; $i<4; $i++) { 
    $pool->submit(new Work($i));
}
$retArr = $pool->process();
usort($retArr, 'sortResultsByTime'); // sort the results by time

// echo out the sorted results
echo "<br><br>";
for($i=0;$i<count($retArr);$i++){
    echo number_format($retArr[$i][0], 4, ".", "").' '.$retArr[$i][1]."\n";
}

function sortResultsByTime($a, $b) {
    return $a[0] > $b[0];
}
?>

请注意上面的代码对我来说是这样的:

0 started (from inside threaded)
0 is ready (from inside threaded)
1 is ready (from inside threaded)
2 is ready (from inside threaded)
3 is ready (from inside threaded)

1609458117.8764 0 started
1609458117.8776 1 is ready
1609458117.8789 2 is ready
1609458117.8802 3 is ready
1609458120.8765 0 is ready

正如预期的那样,从线程内部回显的内容看起来很奇怪,但是如果您存储结果,并按执行时间对它们进行排序,您可以看到它按预期运行。