检测按顺序完成的线程

时间:2015-12-24 07:27:16

标签: php multithreading pthreads

我有以下multethreaded代码

class My_Thread extends Thread {

    public function run() {

        /* ... */
        // The time it takes to execut the code is different for each thread
    }
}

// Create a array
$threads = array();


/* *** STEP 1 *** */
//Initiate Miltiple Threads
foreach ( range("A", "B") as $i ) {
    $threads[] = new My_Thread($i);
}


/* *** STEP 2 *** */
// Start The Threads
foreach ($threads as $thread) {
    $thread->start(); // Thread A starts before thread B, and it takes more time to finish
}


/* *** STEP 3 *** */
// Process the threads
foreach ($threads as $thread) {
    if ($thread->join()) {
        /* ... Do Something ... */
    }
}

快速解释代码:

第1步:我正在创建两个线程,A和B

步骤2:首先启动线程A,并且比线程B花费更长的时间。

步骤3:然后,我正在等待每个线程完成,从线程A开始。

现在,问题出现在第3步。当我遍历线程时,我必须等待线程A完成才能进行进一步的processig,但是,线程B正在等待处于空闲状态,因为它需要更短的时间完成,除非在步骤3中处理了线程A,否则不会被处理。不能保证线程A需要更长的时间,所以我必须编写一个通用的解决方案。

如何确保第3步处理先完成的线程?换句话说,有这样的伪代码吗?

/* *** STEP 3 *** */
// Do the following for all threads in the $threads array, FIRST COME FIRST SERVE
// If the thread finished STEP 2, then immediately process it.

感谢。

1 个答案:

答案 0 :(得分:3)

首先,Thread表示执行上下文

您需要做的是分别考虑上下文和数据......

<?php
class Test extends Thread {

    public function __construct(Volatile $queue, $value) {
        $this->queue = $queue;
        $this->value = $value;
    }

    public function run() {
        $data = strlen(
            file_get_contents("http://www.google.co.uk/?q={$this->value}"));

        usleep(mt_rand(10000, 20000));

        $this->queue->synchronized(function($queue, $value, $data) {
            $queue[] = (array) [
                $value => $data
            ];
            $queue->notify();
        }, $this->queue, $this->value, $data);
    }

    private $queue;
    private $value;
}

$chars = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"];
$queue = new Volatile();
$tests = [];

for ($test = 0; $test < 10; $test++) {
    $tests[$test] = new Test($queue, $chars[$test]);
    $tests[$test]->start();
}

$test = 0;

while (($next = $queue->synchronized(function() use($queue, &$test) {
    /* guard infinite loop */
    if (++$test > 10)
        return false;

    /* predicated wait for data */
    while (!count($queue))
        $queue->wait();

    /* return next item */
    return $queue->shift();
}))) {
    var_dump($next);
}

foreach ($tests as $thread)
    $thread->join();
?>

上面的代码适用于pthreads v3,PHP7,这是可用的最佳版本,以及用于新项目的推荐版本。

解决方案的内容包含在Test::run和主环境中的while循环中。

$data = strlen(
    file_get_contents("http://www.google.co.uk/?q={$this->value}"));

usleep(mt_rand(10000, 20000));

这是为了从google中获取一些垃圾,碰巧响应时间非常一致,我必须添加usleep,这样才能看到顺序无关紧要。

您不应该在真实世界的多线程代码中使用usleep

$this->queue->synchronized(function($queue, $value, $data) {
    $queue[] = (array) [
        $value => $data
    ];
    $queue->notify();
}, $this->queue, $this->value, $data);

生成一些数据后,每个Test与队列同步,向其附加一些数据,并向当前正在等待的任何上下文发送通知。

与此同时,这种情况正在发生:

$test = 0;

while (($next = $queue->synchronized(function() use($queue, &$test) {
    /* guard infinite loop */
    if (++$test > 10)
        return false;

    /* predicated wait for data */
    while (!count($queue))
        $queue->wait();

    /* return next item */
    return $queue->shift();
}))) {
    var_dump($next);
}

主要上下文与队列同步,而在同步块中,它防止无限循环(因为我们知道有多少数据将来),那么如果队列中没有数据,它将等待某些数据变为可用。最后将队列中的第一项返回到主上下文。

上面的代码将输出如下内容:

array(1) {
  ["I"]=>
  int(188965)
}
array(1) {
  ["B"]=>
  int(188977)
}
array(1) {
  ["C"]=>
  int(188921)
}
array(1) {
  ["F"]=>
  int(188962)
}
array(1) {
  ["J"]=>
  int(188954)
}
array(1) {
  ["A"]=>
  int(188912)
}
array(1) {
  ["E"]=>
  int(188929)
}
array(1) {
  ["G"]=>
  int(188941)
}
array(1) {
  ["D"]=>
  int(188946)
}
array(1) {
  ["H"]=>
  int(188929)
}

这里的关键是上下文和数据是不同的问题。