前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >PHP官方真正的异步要来了吗?

PHP官方真正的异步要来了吗?

作者头像
Tinywan
发布2025-03-04 21:48:37
发布2025-03-04 21:48:37
9200
代码可运行
举报
文章被收录于专栏:开源技术小栈开源技术小栈
运行总次数:0
代码可运行

引言

多年来,PHP一直致力于在长期运行的应用程序开发领域拓展版图,在这一领域中,并发代码执行技术发挥着至关重要的作用。目前,诸如WokermanSwooleAMPHPReactPHP等可投入生产环境的解决方案已相继问世。

尽管如此,PHP在编写并发代码方面仍缺乏一套全面的实现机制。即便部分PHP扩展具备相应能力,却也无法支持非阻塞执行。以Swoole为例,为了实现少量功能的修改,它不得不复制数千行代码;而AMPHP开发人员则需要在用户空间中,针对MySQLPostgreSQLRedis等系统,从零开始构建驱动程序。

本RFC(请求评论)的核心目标,是确立一套使用PHP编写并发代码的标准,同时打造一个C API接口。借助该接口,开发者能够运用CRustC++等多种语言,在底层对PHP进行扩展。如此一来,扩展程序便可支持非阻塞I/O功能,并且无需重写PHP函数或重复编写代码。

目标

  • 从 PHP 开发人员的角度来看,这种实现的主要价值是他们不需要更改现有代码(或者如果需要更改,它们应该是最少的)来启用并发。与显式异步模型不同,这种方法允许开发人员在纤程中重用现有的同步代码,而无需修改。
  • 最初编写并打算在 Fiber 外部运行的代码必须在 Fiber 内部工作,无需修改。
  • PHP 开发人员不必考虑 Fibers 如何切换,也不需要管理他们的切换 — 除非在特殊情况下,他们有意识地选择干预此逻辑。
  • PHP 开发者无需考虑协程如何切换,也无需管理它们的切换,除非在特殊情况下,他们有意识地选择干预这一逻辑。
  • 如果存在现有代码或常见风格,如 AMPHP 接口、Go 语言的协程、Swoole API 等,最好采用广大开发者最熟悉的方式。
  • 目标是在灵活性和简单性之间找到平衡。一方面,该实现应允许利用现有解决方案,而无需依赖外部库;另一方面,应避免不必要的复杂性。此实现中的许多设计选择,都是为了让开发者摆脱对 “外部库” 兼容性的担忧,转而采用标准化方法。
  • 真正的异步旨在通过提供一个面向对象的接口,抽象掉事件循环管理,让开发者专注于对象生命周期,而不是资源管理或实现细节。内存管理以及诸如ResumeNotifierCallback等对象的生命周期,是隐藏复杂性的关键工具,确保开发者无需担心手动释放资源。

提案

隐式模型

开源技术小栈在语言抽象层面实现异步有两种模型

  • 显式模型:使用 Promise/Future 以及 awaitasync。语言明确定义哪些函数可以是异步的。异步函数必须返回一个类似 Promise 的结果。
  • 隐式模型 或 透明模型(如在 Go、Erlang 或 Elixir 中看到的):函数不会被显式标记为同步或异步。任何函数都可以异步调用。

标准

显式模型

隐式模型

意图清晰度

代码清晰地描述了程序员的意图

允许以同步风格编写大部分代码

可预测性

程序员始终知道特定函数的行为

需要额外努力来确定上下文切换

执行控制

需要显式规划执行流程

可能导致错误和额外的调试工作

开源技术小栈真正的异步实现了隐式异步模型,这样做有几个原因:

  • 最小化代码和语言语法的更改(当前 RFC 中没有语法更改)。
  • 可重用性 —— 隐式模型有助于避免重写最初为顺序执行设计的代码。
  • 像 Swoole 这样的解决方案也提供了隐式模型,并且已经在实践中得到了验证。

开源技术小栈真正的异步明确定义了两个组件:

  • Scheduler(调度器)—— 负责执行协程(Fibers)。
  • Reactor(反应器)—— 负责事件循环。

在正常模式下,代码在协程(Fiber)之外执行,不会发生任何变化。当调用阻塞函数,如sleep()shell_exec()fread()时,它的行为与平常一样:执行会暂停,直到操作完成。

然而,一旦使用 API 创建了一个或多个协程(Fibers),并且激活了调度器(Scheduler),代码就会开始并发执行。

代码语言:javascript
代码运行次数:0
复制
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";

预期输出

代码语言:javascript
代码运行次数:0
复制
start
fiber 1: start
fiber 2: start
fiber 1: end
fiber 2: end
end

从 PHP 开发者的角度来看,协程内部的代码与外部的代码没有区别。协程内部代码的行为,与没有使用协程时完全一样。此外,PHP 开发者无需额外努力,就能将控制权从一个协程转移到另一个协程。

sleep函数本身不会执行任何协程切换操作,而是创建一个特殊的Resume对象,该对象负责恢复协程,并将其链接到一个定时器事件。当定时器触发时,Resume对象会更新其状态,协程会被放入队列中,以便稍后执行。

PHP 开发者不应假设协程的执行顺序,因为这个顺序可能会改变,或者过于复杂而难以预测。

RFC

Wiki:https://wiki.php.net/rfc/true_async

Github:https://github.com/EdmondDantes/php-src/tree/async

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-03-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 目标
  • 提案
    • 隐式模型
  • RFC
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档