当前位置: 首页 > 科技观察

关于PHP协程与阻塞的思考

时间:2023-03-13 20:18:02 科技观察

进程、线程、协程关于进程、线程、协程有非常详细和丰富的博客或学习资源。我不会在这里详细介绍。我将简要介绍这些东西。进程有自己独立的堆和栈,堆和栈都不共享,进程由操作系统调度。线程有自己独立的栈和共享堆,共享堆,不是共享栈,线程也是由操作系统调度的(标准线程是的)。协程像线程一样共享堆,但不共享栈。协程由程序员在协程的代码中进行调度。yieldyield在PHP中的基本实现是生成器类,迭代器类是迭代器接口的实现:GeneratorimplementsIterator{publicmixedcurrent(void)//返回当前生成的值publicmixedkey(void)//返回当前生成的密钥(void)//生成器继续执行publicvoidrewind(void)//重置迭代器,如果迭代已经开始,这里会抛出异常。//renwind的执行会导致第一个yield被执行,它的返回值被忽略。publicmixedsend(mixed$value)//给生成器传入一个值,作为yield表达式的结果,然后继续执行生成器。如果调用此方法时生成器//不在yield表达式中,它将在传入值之前运行到第一个yield表达式。publicvoidthrow(Exception$exception)//向生成器抛出异常publicboolvalid(void)//检查迭代器是否关闭publicvoid__wakeup(void)//序列化回调,抛出异常说明生成器无法序列化。}以上分析可以参考PHP官方文档。http://php.net/manual/zh/clas...还有鸟哥翻译的这篇详细文档:http://www.laruence.com/2015/...我会用他来实现更多的协程基于任务调度,我举个例子,谈谈我对阻塞的一些思考。自定义简单定时执行任务示例:(本示例必须依赖上述鸟哥实现的协程调度代码)classtimer{private$start=0;//定时开始时间private$timer;//间隔时间差,单位秒private$value=0;//生成的结果值private$callback;//异步回调private$isEnd=false;//当前定时器任务是否结束publicfunction__construct($timer,callable$callback){$this->start=时间();$this->timer=$timer;$this->callback=$callback;}publicfunctionrun(){if($this->valid()){$callback=$this->callback;$callback($this->value++,$this);$this->start=time();}}/***定时执行检查*/publicfunctionvalid(){$end=time();if($end-$this->开始>=$this->timer){returntrue;}else{returnfalse;}}publicfunctionsetEnd($isEnd){$this->isEnd=$isEnd;}publicfunctiongetEnd(){return$this->isEnd;}}/***模拟阻塞协程1**/functiontaskObject1(){$timer=newtimer(1,function($value,timer$timer){if($value>=5){$timer->setEnd(true);}回声'
'.'A'.$value;});$tid=(yieldgetTaskId());while(true){if($timer->getEnd()==true){break;}yield$timer->run();}}/***模拟阻塞协程2**/functiontaskObject2(){$timer=new定时器(2,函数($value,timer$timer){if($value>=3){$timer->setEnd(true);}echo'
'。'B'。$value;});$tid=(yieldgetTaskId());while(true){if($timer->getEnd()==true){break;}yield$timer->run();}}$scheduler=newScheduler;$scheduler->newTask(taskObject1());$scheduler->newTask(taskObject2());$scheduler->run();上面的实现是:生成两个任务,并行执行,每个任务在执行过程中模拟Block几秒;让协程平滑切换,任务阻塞互不影响;思考:我为什么要做上面的事情?因为我发现协程的实现虽然很强大很有趣,可以让多个任务并行,但是当我在其中一个任务中调用系统函数sleep()时,阻塞任务会阻止协程切换。处理阻塞,但不产生阻塞,看是否可行。PHP本身只是提供了一个生成器来提供对协程调用的支持。如果不依赖扩展,则不提供多线程程序实现方法。没有Java强大,可以通过开子线程来实现。我的印象是java的子线程都是独立执行的,不会互相阻塞,所以我在想,既然PHP可以实现类似多线程的机制,那么调用的时候能不能做到非阻塞呢?经过这样的实现和思考,一开始陷入了一个误区,是PHP的原生函数sleep()阻塞导致的,即认为如果要真正实现非阻塞或者异步,我们必须依赖底层语言。后来想明白了一个道理,既然某个方法或者函数在执行过程中会阻塞,那么把当前的方法换成自定义的,使其成为非阻塞的(相对于整个协程调度)就够了吗?比如我上面实现的定时执行。另一方面,协程调度本身的目的就是将任务执行过程切割成尽可能小的块,从而快速切换执行,实现并行。从这个角度来说,协程也应该算是一种编程思想。下面是一个程序被切割成尽可能小的片段的例子://Asimpleexample