关于PHP协程与阻塞的思考

进程、线程、协程

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

  1. 进程拥有本身独立的堆和栈,既不共享堆,亦不共享栈,进程由操做系统调度。html

  2. 线程拥有本身独立的栈和共享的堆,共享堆,不共享栈,线程亦由操做系统调度(标准线程是的)。java

  3. 协程和线程同样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。程序员

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官方文档。bash

php.net/manual/zh/c…多线程

以及鸟哥翻译的这篇详细文档:异步

www.laruence.com/2015/05/28/…函数

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

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

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

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();复制代码

以上实现的是:

  1. 产生两个任务,并行执行,而且给每一个任务在执行的时候模拟几秒钟的阻塞;

  2. 让协程切换的时候能顺利切换,其中的任务阻塞不相互影响;

思考:

我为何要作以上这件事情呢?由于我发现协程实现虽然很强大也颇有意思,能让多任务并行,可是我在其中一个任务里调用系统函数 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 生成一个很大的整型数组的方式切换为分片执行,也就是说在遍历的时候再去取到指定的值,从代码上来看,内存消耗相对于以前来讲就很是小了。

相关文章
相关标签/搜索