多年来,PHP一直致力于在长期运行的应用程序开发领域拓展版图,在这一领域中,并发代码执行技术发挥着至关重要的作用。目前,诸如Wokerman
、Swoole
、AMPHP
、ReactPHP
等可投入生产环境的解决方案已相继问世。
尽管如此,PHP在编写并发代码方面仍缺乏一套全面的实现机制。即便部分PHP扩展具备相应能力,却也无法支持非阻塞执行。以Swoole为例,为了实现少量功能的修改,它不得不复制数千行代码;而AMPHP开发人员则需要在用户空间中,针对MySQL
、PostgreSQL
、Redis
等系统,从零开始构建驱动程序。
本RFC(请求评论)的核心目标,是确立一套使用PHP编写并发代码的标准,同时打造一个C API
接口。借助该接口,开发者能够运用C
、Rust
、C++
等多种语言,在底层对PHP进行扩展。如此一来,扩展程序便可支持非阻塞I/O功能,并且无需重写PHP函数或重复编写代码。
Resume
、Notifier
和Callback
等对象的生命周期,是隐藏复杂性的关键工具,确保开发者无需担心手动释放资源。开源技术小栈在语言抽象层面实现异步有两种模型
Promise/Future
以及 await
和 async
。语言明确定义哪些函数可以是异步的。异步函数必须返回一个类似 Promise 的结果。标准 | 显式模型 | 隐式模型 |
---|---|---|
意图清晰度 | 代码清晰地描述了程序员的意图 | 允许以同步风格编写大部分代码 |
可预测性 | 程序员始终知道特定函数的行为 | 需要额外努力来确定上下文切换 |
执行控制 | 需要显式规划执行流程 | 可能导致错误和额外的调试工作 |
开源技术小栈真正的异步实现了隐式异步模型,这样做有几个原因:
开源技术小栈真正的异步明确定义了两个组件:
在正常模式下,代码在协程(Fiber)之外执行,不会发生任何变化。当调用阻塞函数,如sleep()
、shell_exec()
或fread()
时,它的行为与平常一样:执行会暂停,直到操作完成。
然而,一旦使用 API 创建了一个或多个协程(Fibers),并且激活了调度器(Scheduler),代码就会开始并发执行。
Async\run(function() {
echo"fiber 1: start\n";
sleep(1);
echo"fiber 1: end\n";
});
Async\run(function() {
echo"fiber 2: start\n";
sleep(1);
echo"fiber 2: end\n";
});
echo"start\n";
Async\launchScheduler();
echo"end\n";
预期输出
start
fiber 1: start
fiber 2: start
fiber 1: end
fiber 2: end
end
从 PHP 开发者的角度来看,协程内部的代码与外部的代码没有区别。协程内部代码的行为,与没有使用协程时完全一样。此外,PHP 开发者无需额外努力,就能将控制权从一个协程转移到另一个协程。
sleep
函数本身不会执行任何协程切换操作,而是创建一个特殊的Resume
对象,该对象负责恢复协程,并将其链接到一个定时器事件。当定时器触发时,Resume
对象会更新其状态,协程会被放入队列中,以便稍后执行。
PHP 开发者不应假设协程的执行顺序,因为这个顺序可能会改变,或者过于复杂而难以预测。
Wiki:https://wiki.php.net/rfc/true_async
Github:https://github.com/EdmondDantes/php-src/tree/async