在 PHP 中运行异步函数

5

是否可能创建一个能够异步运行函数的 PHP 类?以下是我迄今为止所做的:

class Worker extends Thread
{
    protected  $asyncFun;
    protected $paramsArray;

    public function run() {
        $asyncFun(/*parameters go here*/)
    }

    public function setAsyncFunction($func, $paramsArr)
    {
        $this->asyncFun = $func;
        $this->paramsArray = $paramsArr;
    }
}

这是我想要称呼它的方式:

$worker = new Worker();
$worker->setAsyncFunction(foo, ["a", "b"]);
$worker::start();

我会使用exec生成一个新的PHP脚本。 - user557846
但这种方式不是动态的。 - vlio20
你可以这样做,你的类中没有使它异步化,你需要创建一个新实例来完成。或者也许你只是想要http://php.net/manual/en/book.gearman.php 我只能猜测。 - user557846
抱歉Dagon,除非您将输出进行管道处理,否则您的示例不是异步的。 - Scopey
2
从PHP手册中得知:注意: 如果使用此函数启动程序,为了使其在后台继续运行,程序的输出必须被重定向到文件或另一个输出流。如果未能这样做,PHP将一直挂起,直到程序执行结束。- 这是您对异步的定义吗? - Scopey
显示剩余4条评论
2个回答

5

最近版本的pthreads支持闭包作为成员,使得代码非常简单:

<?php
class Background extends Thread {

    public function __construct(callable $call, array $args = []) {
        $this->call = $call;
        $this->args = $args;
    }

    public function run() {
        call_user_func_array($this->call, $this->args);
    }

    protected $call;
    protected $args;
}

$background = new Background(function($greeting){
    printf("%s\n", $greeting);
}, ["Hello World"]);
$background->start();
$background->join();

function named($greeting) {
    printf("%s\n", $greeting);
}

$background = new Background("named", ["Goodbye World"]);
$background->start();
$background->join();
?>

然而,这是很糟糕的,很难想象有任何函数是如此饥饿以至于需要一个自己的线程。

你已经走上了正确的道路,考虑重用上下文并创建一个工作线程,pthreads已经内置了所有这些功能。

更合理的使用内置类的代码看起来更像:

<?php
class Background extends Threaded {

    public function __construct(callable $call, array $args = []) {
        $this->call = $call;
        $this->args = $args;
    }

    public function run() {
        call_user_func_array($this->call, $this->args);
    }

    protected $call;
    protected $args;
}

$pool = new Pool(4);

$pool->submit(new Background(function($greeting){
    printf("%s\n", $greeting);
}, ["Hello World"]));

$pool->shutdown();
?>

但这仍然无法处理返回值。我假设您想要检索使用 Pool 进行的调用的结果,那么代码看起来更像:

<?php
class Background extends Threaded {

    public function __construct(callable $call, array $args = []) {
        $this->call = $call;
        $this->args = $args;
    }

    public function run() {
        $this->synchronized(function(){
            $this->result = call_user_func_array
                ($this->call, $this->args);
            $this->notify();
        });
    }

    public function getResult() {
        return $this->synchronized(function(){
            while (!isset($this->result))
                $this->wait();
            return $this->result;
        });
    }

    protected $call;
    protected $args;
    protected $result;
}

$pool = new Pool(4);

$call = new Background(function($greeting){
    return sprintf("%s\n", $greeting);
}, ["Hello World"]);

$pool->submit($call);

echo $call->getResult();

$pool->shutdown();
?>

正如您所看到的,调用Background::getResult将导致调用上下文等待结果可用,这可能是可取的,也可能不是,但可以作为一个很好的例子。


2

PHP是一种同步语言。几乎任何你做的事情都会导致PHP挂起,直到它完成,包括exec调用,如果你需要响应。

使用核心PHP元素的实现可能需要你执行一些exec或cURL调用,然后在脚本中浏览服务器以获取输出。

你可以使用Dagon提到的PECL(Gearman),但我个人认为使用像beanstalkd这样的队列管理器更容易管理。

这里是beanstalkd的网站。 而这里是一个很好的用于beanstalkd的PHP库(带有一些示例)。


我所知道的默认PHP安装中没有多线程类?Beanstalkd还具有不依赖于PHP并支持多个服务器的优点。您可以在beanstalkd队列中拿出工作的工作者几乎可以用任何语言编写。基本上,它更快且更多样化。 - Scopey
一个PECL扩展的一部分:http://php.net/manual/en/pthreads.installation.php - 你必须自己安装这个库。 - Scopey
好的,那又怎样?这可能会有什么问题吗? - vlio20
我对使用PECL扩展的唯一保留是你必须让它们保持最新状态,这可能不总是与保持PHP最新版本相符。你真的依赖于社区扩展开发者来保持他们的软件最新,以便你可以更新PHP。当然,如果你确定pthread是适合你的正确选择,请不要让我阻止你使用。 - Scopey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接