我正在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
答案 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个任务,可以通过PoolB
,task0
,task1
,task2
来调用它们。
从我的角度来看,当发生循环时,应该是这样的队列
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
提交PoolB
和task1
不需要等待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
正如预期的那样,从线程内部回显的内容看起来很奇怪,但是如果您存储结果,并按执行时间对它们进行排序,您可以看到它按预期运行。