关于PHP中协程和阻塞的一些理解与思考

发布时间 - 2026-01-11 02:44:57    点击率:

前言

本文主要给大家介绍了关于PHP中协程和阻塞的理解与思考,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍:

进程、线程、协程

关于进程、线程、协程,有非常详细和丰富的博客或者学习资源,我不在此做赘述,我大致在此介绍一下这几个东西。

  • 进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
  • 线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。
  • 协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

PHP中的协程实现基础 yield

yield的根本实现是生成器类,而迭代器类是迭代器接口的实现:

Generator implements Iterator {
 public mixed current ( void ) // 返回当前产生的值
 public mixed key ( void ) // 返回当前产生的键
 public void next ( void ) // 生成器继续执行
 public void rewind ( void ) // 重置迭代器,如果迭代已经开始了,这里会抛出一个异常。
   // renwind的执行将会导致第一个yield被执行, 并且忽略了他的返回值.
 public mixed send ( mixed $value ) // 向生成器中传入一个值,并且当做 yield 表达式的结果,然后继续执行生成器。如果当这个方法被调用时,生成器 
   // 不在 yield 表达式,那么在传入值之前,它会先运行到第一个 yield 表达式。
 public void throw ( Exception $exception ) // 向生成器中抛入一个异常
 public bool valid ( void ) // 检查迭代器是否被关闭
 public void __wakeup ( void ) // 序列化回调,抛出一个异常以表示生成器不能被序列化。
}

以上解析可以参考PHP官方文档。

http://php.net/manual/zh/clas...

以及这篇详细文档:

https://www./article/39424_all.htm

我就以他实现的协程多任务调度为基础做一下例子说明并说一下关于我在阻塞方面所做的一些思考。

自定义简单定时执行任务示例:

(此例子必须依赖于以上鸟哥实现的协程调度代码)

class timer {
 private $start = 0; // 定时开始时间
 private $timer; // 间隔的时间差,单位秒
 private $value = 0; // 产生的结果值
 private $callback; // 异步回调
 private $isEnd = false; // 当前定时器任务是否结束
 public function __construct($timer,callable $callback)
 {
 $this->start = time();
 $this->timer = $timer;
 $this->callback = $callback;
 }
 public function run() {
 if($this->valid()) {
 $callback = $this->callback;
 $callback($this->value ++,$this);
 $this->start = time();
 }
 }
 /**
 * 定时执行检查
 */
 public function valid() {
 $end = time();
 if($end - $this->start >= $this->timer) {
 return true;
 } else {
 return false;
 }
 }
 public function setEnd($isEnd) {
 $this->isEnd = $isEnd;
 }
 public function getEnd() {
 return $this->isEnd;
 }
}

/**
 * 模拟阻塞的协程1
 *
 */
function taskObject1() {
 $timer = new timer(1,function($value,timer $timer) {
 if($value >= 5) {
 $timer->setEnd(true);
 }
 echo '<br>'.'A '.$value;
 });
 $tid = (yield getTaskId());
 while (true) {
 if($timer->getEnd() == true) {
 break;
 }
 yield $timer->run();
 }
}
/**
 * 模拟阻塞的协程2
 *
 */
function taskObject2() {
 $timer = new timer(2,function($value,timer $timer) {
 if($value >= 3) {
 $timer->setEnd(true);
 }
 echo '<br>'.'B '.$value;
 });
 $tid = (yield getTaskId());
 while (true) {
 if($timer->getEnd() == true) {
 break;
 }
 yield $timer->run();
 }
}
$scheduler = new Scheduler;
$scheduler->newTask(taskObject1());
$scheduler->newTask(taskObject2());
$scheduler->run();

以上实现的是:

  • 产生两个任务,并行执行,并且给每个任务在执行的时候模拟几秒钟的阻塞;
  • 让协程切换的时候能顺利切换,其中的任务阻塞不相互影响;

思考:

我为什么要做以上这件事情呢?因为我发现协程实现虽然很强大也很有意思,能让多任务并行,但是我在其中一个任务里调用系统函数 sleep() 的时候,阻塞任务会阻止协程切换,其实从协程的实现原理上来书也是这么回事。

那么,我也就想模拟协程阻塞,但是不产生阻塞看是否可行。PHP本身只提供了生成器为协程调用提供了支撑,如果不依赖扩展,没有提供多线程的程序实现方式,没有java那么强大,可以开子线程进行实现。

我印象中java的子线程是独立执行且不会相互阻塞的,所以我在想,PHP既然可以实现类似于多线程这样的机制,那么能不能实现调用过程中非阻塞呢?

经过这样一个实现和思考,一开始是陷入了一个误区的,是由于PHP原生函数 sleep() 阻塞造成的思维误区,那就是认为要想真正实现非阻塞或者说实现异步的话,是必须依赖于语言底层的。

后来,我想明白了一个道理,既然某个方法或者函数在执行过程中,会产生阻塞,那么把当前这个方法换成自定义的,做成非阻塞(相对于整个协程调度来说)不就行了吗?比如上面的定时执行我自己实现了一个。

而另一方面,协程调度本身的目的也是为了把任务执行过程切成尽量小片,从而快速切换执行,达到并行的目的。从这方面来看,协程应该也算是一种程序设计思想。

以下是一个程序切成尽量小片执行的例子:

// 一个简单的例子
<?php
function xrange($start, $end, $step = 1) {
 for ($i = $start; $i <= $end; $i += $step) {
 yield $i;
 }
}
 
foreach (xrange(1, 1000000) as $num) {
 echo $num, "\n";
}

这个例子是把原本用 range 生成一个很大的整型数组的方式切换为分片执行,也就是说在遍历的时候再去取到指定的值,从代码上来看,内存消耗相对于之前来说就非常小了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。


# php  # 协程  # 阻塞  # php7  # php 多进程编程父进程的阻塞与非阻塞实例分析  # 简单介绍PHP非阻塞模式  # PHP实现非阻塞模式的方法分析  # php使用flock阻塞写入文件和非阻塞写入文件的实例讲解  # PHP实现的CURL非阻塞调用类  # PHP编程中的Session阻塞问题与解决方法分析  # php基于session锁防止阻塞请求的方法分析  # 深入解析PHP中的(伪)多线程与多进程  # 深入探究PHP的多进程编程方法  # php多进程中的阻塞与非阻塞操作实例分析  # 我在  # 迭代  # 切成  # 在此  # 自定义  # 相对于  # 抛出  # 回调  # 多线程  # 中非  # 的是  # 小片  # 是一个  # 器中  # 操作系统  # 我也  # 我想  # 文档  # 序列化  # 是一种 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何在IIS7中新建站点?详细步骤解析  Laravel如何实现图片防盗链功能_Laravel中间件验证Referer来源请求【方案】  Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】  Android 常见的图片加载框架详细介绍  宙斯浏览器文件分类查看教程 快速筛选视频文档与图片方法  Laravel如何记录自定义日志?(Log频道配置)  Python文件流缓冲机制_IO性能解析【教程】  BootStrap整体框架之基础布局组件  Laravel如何与Pusher实现实时通信?(WebSocket示例)  Laravel软删除怎么实现_Laravel Eloquent SoftDeletes功能使用教程  如何快速生成ASP一键建站模板并优化安全性?  如何挑选优质建站一级代理提升网站排名?  香港服务器建站指南:免备案优势与SEO优化技巧全解析  焦点电影公司作品,电影焦点结局是什么?  百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭  如何快速建站并高效导出源代码?  公司门户网站制作流程,华为官网怎么做?  JavaScript数据类型有哪些_如何准确判断一个变量的类型  如何快速搭建高效可靠的建站解决方案?  Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】  Laravel项目怎么部署到Linux_Laravel Nginx配置详解  ,在苏州找工作,上哪个网站比较好?  Laravel用户认证怎么做_Laravel Breeze脚手架快速实现登录注册功能  Laravel Eloquent模型如何创建_Laravel ORM基础之Model创建与使用教程  Laravel如何实现数据库事务?(DB Facade示例)  JavaScript中的标签模板是什么_它如何扩展字符串功能  谷歌Google入口永久地址_Google搜索引擎官网首页永久入口  矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?  Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】  Swift开发中switch语句值绑定模式  如何用免费手机建站系统零基础打造专业网站?  如何用虚拟主机快速搭建网站?详细步骤解析  Laravel Facade的原理是什么_深入理解Laravel门面及其工作机制  Laravel定时任务怎么设置_Laravel Crontab调度器配置  Laravel的辅助函数有哪些_Laravel常用Helpers函数提高开发效率  学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?  Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析  Linux网络带宽限制_tc配置实践解析【教程】  Laravel如何实现多语言支持_Laravel本地化与国际化(i18n)配置教程  详解Huffman编码算法之Java实现  网站设计制作书签怎么做,怎样将网页添加到书签/主页书签/桌面?  Linux后台任务运行方法_nohup与&使用技巧【技巧】  Laravel Admin后台管理框架推荐_Laravel快速开发后台工具  作用域操作符会触发自动加载吗_php类自动加载机制与::调用【教程】  详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)  Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)  香港代理服务器配置指南:高匿IP选择、跨境加速与SEO优化技巧  Laravel如何正确地在控制器和模型之间分配逻辑_Laravel代码职责分离与架构建议  如何快速搭建安全的FTP站点?  进行网站优化必须要坚持的四大原则