在PHP中卸载一次性工作线程的最佳方法?并行线程? fcntl函数?

时间:2015-02-13 21:56:20

标签: php multithreading pcntl

我应该多线程一些需要超时的php-cli代码?

我从命令行在Centos 6.6上使用PHP 5.6。

我对多线程术语或代码不是很熟悉。我在这里简化了代码,但它完全代表了我想要做的事情。

非线程代码目前看起来像这样:

$datasets = MyLibrary::getAllRawDataFromDBasArrays();
foreach ($datasets as $dataset) {
    MyLibrary::processRawDataAndStoreResultInDB($dataset);
}
exit; // just for clarity

我需要预取所有数据集,并且每个processRawDataAndStoreResultInDB()都无法获取它自己的数据集。有时processRawDataAndStoreResultInDB()处理数据集的时间太长,所以我想限制它处理数据集的时间。

所以你可以看到让它成为多线程的

  1. 通过允许多个processRawDataAndStoreResultInDB()同时执行来加速它
  2. 使用set_time_limit()限制每个数据集处理每个数据集所需的时间
  3. 请注意,我不需要回到我的主程序。由于这是一种简化,您可以相信我不想收集所有已处理的数据集,并在完成后对数据库进行一次保存。

    我想做类似的事情:

    class MyWorkerThread extends SomeThreadType {
      public function __construct($timeout, $dataset) {
        $this->timeout = $timeout;
        $this->dataset = $dataset;
      }
    
      public function run() {
        set_time_limit($this->timeout);
        MyLibrary::processRawDataAndStoreResultInDB($this->dataset);
      } 
    }
    
    $numberOfThreads = 4;
    $pool = somePoolClass($numberOfThreads);
    $pool->start();
    
    $datasets = MyLibrary::getAllRawDataFromDBasArrays();
    $timeoutForEachThread = 5; // seconds
    foreach ($datasets as $dataset) {
      $thread = new MyWorkerThread($timeoutForEachThread, $dataset);
    
      $thread->addCallbackOnTerminated(function() {
        if ($this->isTimeout()) {
          MyLibrary::saveBadDatasetToDb($dataset);
        }
      }
    
      $pool->addToQueue($thread);
    }
    
    $pool->waitUntilAllWorkersAreFinished();
    exit; // for clarity
    

    从我的在线研究中我发现了PHP扩展pthreads,我可以使用我的线程安全的php CLI,或者我可以使用PCNTL扩展或围绕它的包装库(比如,Arara / Process)< / p>

    当我看到它们和它们的例子时(特别是pthreads池示例)我很快就被术语和我应该使用哪些类来实现我正在寻找的那种多线程所困惑。

    如果我在线程类上有isRunning(),isTerminated(),getTerminationStatus()和execute()函数,我甚至不介意自己创建池类,因为它是一个简单的队列。

    有经验的人可以指导我使用哪些库,类和函数来映射上面的示例吗?我完全采取了错误的做法吗?

    提前致谢。

1 个答案:

答案 0 :(得分:0)

这是一个使用工作进程的示例。我正在使用pcntl扩展程序。

/**
 * Spawns a worker process and returns it pid or -1 
 * if something goes wrong.
 *
 * @param callback function, closure or method to call
 * @return integer
 */
function worker($callback) {
    $pid = pcntl_fork();
    if($pid === 0) {
        // Child process
        exit($callback());
    } else {
        // Main process or an error
        return $pid;
    }
}


$datasets = array(
    array('test', '123'),
    array('foo', 'bar')
);

$maxWorkers = 1;
$numWorkers = 0;
foreach($datasets as $dataset) {
    $pid = worker(function () use ($dataset) {
        // Do DB stuff here
        var_dump($dataset);
        return 0;
    });

    if($pid !== -1) {
        $numWorkers++;
    } else {
        // Handle fork errors here
        echo 'Failed to spawn worker';
    }

    // If $maxWorkers is reached we need to wait
    // for at least one child to return
    if($numWorkers === $maxWorkers) {
        // $status is passed by reference
        $pid = pcntl_wait($status);
        echo "child process $pid returned $status\n";
        $numWorkers--;
    }
}

// (Non blocking) wait for the remaining childs
while(true) {
    // $status is passed by reference
    $pid = pcntl_wait($status, WNOHANG);

    if(is_null($pid) || $pid === -1) {
        break;
    }

    if($pid === 0) {
        // Be patient ...
        usleep(50000);
        continue;
    }

    echo "child process $pid returned $status\n";
}