在PHP中的协同程序?

时间:2012-10-17 16:50:46

标签: php join yield state-machine coroutine

您好我正在寻找一种在php文件中实现协同程序的方法。我的想法是,我有很长的流程需要能够在几小时或几天内收益。因此,其他php文件将调用与协程相同的文件中的函数来更新内容,然后调用类似$coroutine.process()的函数,使协同程序从其上一次收益继续。这是为了避免使用大型状态机。

我认为coroutine php文件在空闲时实际上不会运行,但是当给定处理时间时,将从顶部进入并使用类似switch或goto的东西从之前的yield返回。然后,当它到达下一个yield时,文件将保存其当前状态(如会话或数据库),然后退出。

有没有人听说过这个,或者类似的比喻?在一个集合下以某种方式聚合和管理多个协同程序的奖励点,可能支持类似线程的连接,以便当它们完成时流程在一个地方继续(有点像Go)。

更新:php 5.5.0增加了对生成器和协同程序的支持:

https://github.com/php/php-src/blob/php-5.5.0/NEWS

https://wiki.php.net/rfc/generators

我还没有尝试过,所以也许有人可以提出一个准确的例子。我正在尝试将状态机转换为协同程序。因此,例如,for循环内部的切换命令(其流程难以遵循,并且随着更多状态被添加而容易出错)转换为协作线程,其中每个决策点在有序的线性流中容易看到,其暂停yield关键字的状态更改。

这方面的一个具体例子是,假设您正在编写电梯控制器。不是根据电梯的状态(STATE_RISING,STATE_LOWERING,STATE_WAITING等)确定是否读取按钮的状态,而是编写一个循环,其中包含在电梯处于每个状态时运行的子循环。因此,当它上升时,它不会降低,除了紧急按钮之外它不会读取任何按钮。这可能看起来不是什么大问题,但在像聊天服务器这样的复杂状态机中,如果不引入细微的错误,几乎不可能更新状态机。而协作线程(协同程序)版本具有明显可见的流程,更容易调试。

3 个答案:

答案 0 :(得分:2)

PHP不支持协同程序。

我会用setcontext()编写PHP扩展,当然假设你的目标是Unix平台。

这里有一个关于PHP扩展入门的StackOverflow问题:Getting Started with PHP Extension-Development

为什么setcontext()?一个鲜为人知的事实是setcontext()可以用于协同程序。只需在调用另一个协同程序时交换上下文。

答案 1 :(得分:0)

我正在写第二个答案,因为PHP协程似乎有不同的方法。

使用Comet HTTP响应是长期存在的。小<script>个块会不时发送,JavaScript会在到达时由浏览器执行。响应可以暂停等待事件很长时间。 2001我用Java编写了一个小型业余爱好聊天服务器,利用这种技术。我在国外呆了半年,想家了,用它在家里跟我父母和朋友聊天。

聊天服务器向我显示HTTP请求可能会触发其他HTTP响应。这有点像协程。所有HTTP响应都在等待事件,如果事件适用于响应,它会在触发其他响应之后接受处理然后再次进入休眠状态。

您需要一个 medium ,PHP“进程”可以相互通信。一个简单的媒介是文件,但我认为数据库更适合。我的旧聊天服务器使用了日志文件。聊天消息被附加到日志文件中,并且所有聊天进程在无限循环中不断地从日志文件的末尾读取。 PHP支持sockets进行直接通信,但这需要不同的设置。

首先,我提出了这两个功能:

function get_message() {
    # Check medium. Return a message; or NULL if there are no messages waiting.
}

function send_message($message) {
    # Write a message to the medium.
}

您的协程循环如下:

while (1) {
    sleep(1); // go easy on the CPU
    $message = get_message();
    if ($message === NULL) continue;

    # Your coroutine is now active. Act on the message.
    # You can send send messages to other coroutines.
    # You also can send <script> chunks to the browser, like this:
    echo '<script type="text/javascript">';
    echo '// Your JavaScript code';
    echo '</script>';
    flush();

    # Yield
}    

要使用continue,因为它会重新启动等待消息的while (1)循环。协程也在循环结束时产生。

你可以给你的协程ID和/或设计一个订阅模型,其中一些协同程序会收听一些消息,但不是全部。

修改

可悲的是,PHP和Apache并不适合可扩展的解决方案。即使大部分时间协程都没有做任何事情,它们也会将内存占用为进程,如果有太多内存,Apache会开始破坏内存,可能只有几千个协同程序。 Java不是很好,但由于我的聊天服务器是私有的,我没有遇到性能问题。从来没有超过10个用户同时访问它。

Ningx,Node.js或Erlang以更好的方式解决了这个问题。

答案 2 :(得分:0)

Swoole Coroutine库为PHP提供了类似于协程的功能。每个协程每个过程仅添加8K内存。它提供了一个协程API,具有预期的基本功能(例如yield和resume),诸如coroutine迭代器之类的coro实用程序以及诸如文件系统功能和联网(套接字客户端和服务器,redis客户端和服务器,MySQL)等高级协程内置函数客户等)。

问题的第二个要素是拥有长寿的协程的能力-除非您在会话中保存coro的状态并允许coro结束/关闭,否则这可能不是一个好主意。否则,该请求必须与协程一样长。如果该服务由长期运行的PHP脚本托管,则情况会更容易,协程将一直存在直到被允许/强制关闭为止。

Swoole在性能上与基于Node.js和Go的服务相当,并且在定期托管500K + TCP连接的多种生产服务中使用。它是PHP鲜为人知的瑰宝,主要是因为它是在中国开发的,并且大多数支持和文档仅限于讲中文的人,尽管少数人努力帮助说其他语言的人。

Swoole的一个优点是,它的PHP类包装了一个广泛的C / C ++ API,从一开始就设计为允许在不使用PHP的情况下使用其所有功能。可以轻松地将同一源代码编译为* NIX系统和Windows的PHP扩展和/或标准库。