PHP异步编程为:如何将PHP异步编程优化为高效模式?

摘要:PHP 的异步编程 该怎么选择 PHP 的传统执行模型是同步的,这意味着代码按照语句出现的顺序逐条执行。这本身并非问题,因为同步思维往往更为简单。 当要求 PHP 开发者实现 SQL 分页展示时,他们通常会先执行一条统计总数的查询,再执行第
PHP 的异步编程 该怎么选择 PHP 的传统执行模型是同步的,这意味着代码按照语句出现的顺序逐条执行。这本身并非问题,因为同步思维往往更为简单。 当要求 PHP 开发者实现 SQL 分页展示时,他们通常会先执行一条统计总数的查询,再执行第二条查询获取当前页的数据。总记录数对于生成分页链接(首页、下一页、末页等)是必需的。 当 SQL 服务器处理第一条计数查询时,PHP 服务器处于等待状态,收到响应后才执行第二条查询。 当然,存在一次性获取两种信息的方法,但那不是本文的主题,请保持专注。 从这个分页示例中,我们可以看到潜在的优化空间:在 SQL 服务器处理第一条查询的同时启动第二条查询。但要注意,在拿到计数结果之前我们不会显示分页链接,因此即使计数查询先完成,也需要等待另一条查询的结果。 由此可见,异步操作的管理不仅限于并行执行任务,还包括管理响应的处理顺序。 存在许多需要异步执行代码的场景,这通常与 I/O 操作相关:HTTP 请求、数据库访问、文件读写或启动外部进程。 PHP 是异步的吗? 要判断 PHP 是否"异步",首先需要理解"异步"的含义。异步指的是:不同时发生。当某项操作耗时时,与其等待完成,不如先去做其他事情,等操作完成后再回来继续。因此,异步的核心在于操作是非阻塞的。 人们常常混淆异步和并行。 打个比方:异步如同一位厨师将锅接满水放在灶台上开火,趁水烧开的工夫去切蔬菜。等蔬菜切好、水也烧开,就开始烹饪。 并行则是两位厨师:一位切蔬菜的同时,另一位负责烧水。蔬菜切好、水也烧开后,由第一位厨师负责烹饪。 并行节省了时间,因为切蔬菜与烧水准备是同时进行的。但两种模式下,水烧开的过程中都可以去做其他事情。 具体而言,我们的"厨师"就是机器的 CPU/GPU。 PHP 的异步能力 从 2002 年 PHP 4.3 发布起,一项重要功能被引入:Streams。通过 stream_set_blocking() 和 stream_select() 函数,PHP 进入了异步编程时代。 $h = fopen(__FILE__, 'r'); stream_set_blocking($h, false); $content = ''; while (!feof($h)) { $read = array($h); $write = $except = null; // 检查是否有可读内容,最多等待 1000 微秒 // 永远不要设为 0,否则会导致 CPU 过度占用 $ready = stream_select($read, $write, $except, 1000); if ($ready === 0) { // 没有可读内容,稍作等待 // 或者去做其他事情... usleep(1000); continue; } $chunk = fgets($h, 1024); if ($chunk !== false) { $content .= $chunk; } } fclose($h); echo $content; 注意,这段示例代码刻意简化,未处理错误等情况。 在 usleep(1000) 的位置,可以执行其他操作,比如读取另一个文件,甚至向其他服务器发起 HTTP 请求。不过,如果你的文件系统很快,可能不会进入等待时间。这种技术更适合处理慢速文件系统或其他类型的 I/O 操作。 23 年前 PHP 就已支持异步编程,然而几年前人们还说 PHP 不是异步语言,为什么? 因为实现异步不仅仅是启动非阻塞处理,还需要有机制来管理这些等待时间。 这就引入了协程的概念。协程是一种可以被挂起、之后恢复的函数。 协程与 Fiber 2013 年 6 月,PHP 5.5 引入生成器(Generators)后,开发者开始将其改造为协程使用。
阅读全文