php-yield关键字以及协程的实现

php yield关键字以及协程的实现

php的yield是在php5.5版本就出来了,而在初级php界却很少人提起,接下来简单说说个人对php yield的理解

Iterator接口(迭代器接口)

在php中,除了数组、对象可以被foreach遍历之外,还有另外一种特殊对象,也就是继承了Iterator接口的对象,也可以被对象遍历,但和普通对象的遍历又有所不同,下面是3种类型的遍历情况:

yield
yield
yield

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$arr = array(
'a' => 1,
'b' => 2,
'c' => 3
);

foreach ($arr as $key => $value) {
echo "数组遍历\n";
var_dump($key, $value);
}
return;
1
2
3
4
5
6
7
8
9
10
11
<?php
class Obj
{
public $a = 1, $b = 2, $c = 3;
public function a() {}
}
foreach (new Obj() as $key => $value) {
echo "对象遍历\n";
var_dump($key, $value);
}
return;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
class Iteratorobj implements Iterator {
protected $data;
public function __construct($arr) {
$this->data = $arr;
}
public function rewind() {
// TODO: Implement rewind() method.
echo "指针重置\n";
reset($this->data);
}
public function current() {
// TODO: Implement current() method.
echo "当前指针数据\n";
return current($this->data) . "自定义遍历值";
}
public function next() {
// TODO: Implement next() method.
echo "指针下移\n";
next($this->data);
}
public function key() {
// TODO: Implement key() method.
echo "返回当前数组键值\n";
return key($this->data);
}
public function valid() {
// TODO: Implement valid() method.
echo "检查迭代指针是否正常\n";
return current($this->data);
}
}
foreach (new Iteratorobj(array(
'a' => 1,
'b' => 2,
'c' => 3,
)) as $key => $value) {
echo "迭代器自定义遍历";
var_dump($key, $value);
}

可以看出,迭代器的遍历,会依次调用重置,检查当前数据,返回当前数据,返回当前指针数据,指针下移方法,结束遍历的条件在于检查数据返回true或者false。

生成器

生成器和迭代器类似,但也不完全相同。
生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组,那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样,和普通函数只返回一次不同的是,生成器可以根据需要 yield 多次,以便生成需要迭代的值。生成器使用yield关键字进行生成迭代的值。

例如:
yield

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
/**
* 函数内部有yield关键字,将返回一个生成器对象来进行迭代
* @param $start
* @param $limit
* @param int $step
* @return Generator
*/
function xrange($start, $limit, $step = 1) {
if ($start < $limit) {
if ($step <= 0) {
throw new LogicException("步进值必须大于0");
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('步进值必须小于0');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
var_dump(xrange(1, 9, 2));
foreach (xrange(1, 9, 2) as $number) {
echo "{$number} ";
}

一、生成器方法

生成器它的内部实现了以下方法:

1
2
3
4
5
6
7
8
9
10
11
Generator implements Iterator { // 返回当前产生的值
public mixed current(void) // 返回当前产生的键
public mixed key(void) // 生成器继续执行
public void next(void) // 重置迭代器,如果迭代已经开始了,这里会抛出一个异常。
public void rewind(void) // 向生成器中传入一个值,当前yield接收值,然后继续执行下一个yield
public mixed send(mixed $value) // 向生成器中抛出一个异常
public void throw(Exception $exception) // 检查迭代器是否被关闭,已经关闭返回 FALSE,否则返回 TRUE
public bool valid(void) // 序列化回调
public void __wakeup(void) // 返回generator函数的返回值,PHP version 7+
public mixed getReturn(void)
}

二、语法

生成器的语法有很多种用法,需要一一说明,首先,

yield必须有函数包裹,包裹yield的函数称为“生成器函数”,该函数将返回一个可遍历对象。

1、颠覆常识的yield

yield

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
function Generator() {
for ($i = 0; $i < 3; $i++) {
echo "输出存在感1\n";
yield $i;
echo "输出存在感2\n";
}
}
echo "#######返回对象#######\n";
var_dump((Generator())); // 返回对象

echo "#######返回对象#######\n\n\n";
echo "#######遍历一次情况#######\n";
foreach (Generator() as $value) {
var_dump($value);
break; // 只便利一次情况
}
echo "#######遍历一次情况#######\n\n\n";

echo "#######一直遍历情况#######\n";
foreach (Generator() as $value) {
var_dump($value); // 遍历多次
}
echo "#######一直遍历情况#######\n";

可能在这里你发现了几个东西,和之前php完全不同的认知,如果你没有发现,那我简单说下:

  • 1、在调用函数返回的时候,可以发现for里面的语句并没有执行
  • 2、在遍历一次的时候,可以发现调用函数,却没有正常的for循环3次,只循环了一次
  • 3、在遍历一次的时候,“存在感2”竟然没有调用,在一直遍历的情况下才调用

再看看另一个例子:

yield
yield
yield

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
function Generator() {
while (true) {
echo yield . "\n";
echo yield . "\n";
echo "哈哈\n";
return '啦啦啦';
}
echo '嗯嗯嗯';
return '返回值';
}
$obj = Generator();
var_dump($obj);
$obj->send(1); // 返回对象
$obj->send(2);
$obj->send(3);
$obj->send(4);
$obj->send(5);
$obj->send(6);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
function Generator() {
while (true) {
echo yield . "\n";
echo yield . "\n";
echo "哈哈\n";
// return '啦啦啦';
}
echo '嗯嗯嗯';
return '返回值';
}
$obj = Generator();
var_dump($obj);
$obj->send(1); // 返回对象
$obj->send(2);
$obj->send(3);
$obj->send(4);
$obj->send(5);
$obj->send(6);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
function Generator() {
while (true) {
echo yield . "\n";
echo yield . "\n";
echo "哈哈\n";
break;
}
echo '嗯嗯嗯';
return '返回值';
}
$obj = Generator();
var_dump($obj);
$obj->send(1); // 返回对象
$obj->send(2);
$obj->send(3);
$obj->send(4);
$obj->send(5);
$obj->send(6);

什么???while(true)竟然还能正常的执行下去???没错,生成器函数就是这样的,根据这个例子,我们发现了这些东西:

  • 1、while(true)没有阻塞调用函数下面的代码执行,却导致了下面的echo “额额额” 和 return 无法执行
  • 2、return 返回值竟然是没有作用的
  • 3、send(1)时,没有echo “哈哈”,send(2)时,才开始出现 “哈哈”。

2、yield的其他语法

yield表达式中,可以赋值,但赋值需要使用括号包裹:
yield

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function Generator() {
while (true) {
$data = (yield);
echo $data . "\n";
echo $data . "\n";
echo "哈哈\n";
// break;
}
echo '嗯嗯嗯';
return '返回值';
}
$obj = Generator();
var_dump($obj);
$obj->send(1); // 返回对象
$obj->send(2);
$obj->send(3);
$obj->send(4);
$obj->send(5);
$obj->send(6);

只需要在表达式后面加上$key => $value,即可生成键值的数据:
yield

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
function Generator() {
for ($i = 0; $i < 3; $i++) {
echo "输出存在感1\n";
yield "key_{$i}" => $i;
echo "输出存在感2\n";
}
}
echo "#######返回对象#######\n";
var_dump((Generator())); // 返回对象

echo "#######返回对象#######\n\n\n";
echo "#######遍历一次情况#######\n";
foreach (Generator() as $value) {
var_dump($value);
break; // 只便利一次情况
}
echo "#######遍历一次情况#######\n\n\n";
echo "#######一直遍历情况#######\n";
foreach (Generator() as $key => $value) {
var_dump($key, $value); // 遍历多次
}
echo "#######一直遍历情况#######\n";

在函数前增加引用定义,就可以像returning reference from functions(从函数返回一个引用)一样引用生成值
yield

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
function &gen_reference() {
$value = 3;
while ($value > 0) {
yield $value;
}
}

/**
* 我们可以在循环中修改$number的值,而生成器是使用的引用值来生成,所以gen_reference()内部的$value值也会跟着变化。
*/
foreach (gen_reference() as &$number) {
echo (--$number) . "\n";
}

三、特性总结

1、yield是生成器所需要的关键字,必须在函数内部,有yield的函数叫做“生成器函数”

2、调用生成器函数时,函数将返回一个继承了Iterator的生成器

3、yield作为表达式使用时,可将一个值加入到生成器中进行遍历,遍历完会中断下面的语句运行,并且保存状态,当下次遍历时会继续执行(这就是while(true))没有造成阻塞的原因)

4、当send传入参数时,yield可作为一个变量使用,这个变量等于传入的参数

协程

一、实现一个简单的协程

协程,是一个编程逻辑的转变,使多个任务能交替运行,而不是之前的一直根据流程往下走,举个例子,当有一个逻辑,每次调用这个文件时,该文件要做3件事:

1、写入300个文件
2、发送邮件给500个会员
3、插入100条数据

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
function task1() {
for ($i = 0; $i <= 300; $i++) {
// 写入文件,大概要3000微秒
usleep(3000);
echo "写入文件{$i}\n";
}
}
function task2() {
for ($i = 0; $i <= 500; $i++) {
// 发送邮件给500名会员,大概3000微秒
usleep(3000);
echo "发送邮件{$i}\n";
}
}
function task3() {
for ($i = 0; $i <= 100; $i++) {
// 模拟插入100条数据,大概3000微秒
usleep(3000);
echo "插入数据{$i}\n";
}
}
task1();
task2();
task3();

这样,就实现了这3个功能了,然而,技术组长又说:能不能改成交替运行呢?就是说,写入文件一次之后,马上去发送一次邮件,然后再去插入一条数据。

然后我该改一改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
function task1($i) {
// 使用$i标识,写入文件,大概要3000微秒
if ($i > 300) {
return false; // 超过300不用写了
}
echo "写入文件{$i}\n";
usleep(3000);
return true;
}
function task2($i) {
// 使用$i标识,发送邮件,大概要3000微秒
if ($i > 500) {
return false; // 超过500不用发送了
}
echo "发送邮件{$i}\n";
usleep(3000);
return true;
}
function task3($i) {
// 使用$i标识,插入数据,大概要3000微秒
if ($i > 100) {
return false; // 超过100不用插入
}
echo "插入数据{$i}\n";
usleep(3000);
return true;
}
$i = 0;
$task1Result = true;
$task2Result = true;
$task3Result = true;
while (true) {
$task1Result && $task1Result = task1($i);
$task2Result && $task2Result = task2($i);
$task3Result && $task3Result = task3($i);
if ($task1Result === false && $task2Result === false && $task3Result === false) {
break; // 全部任务完成,退出循环
}
$i++;
}

运行一下:
代码1:
yield

代码2:
yield

确实是实现了任务交替执行,但是代码2明显让代码变得非常的难读,扩展性也很差,那么,有没有更好的方式实现这个功能呢?这时候我们就必须借助yield了。

首先,我们得封装一个任务类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
/**
* 任务对象
* Class Task
*/
class Task {
protected $taskId; // 任务id
protected $coroutine; // 生成器
protected $sendValue = null; // 生成器send值
protected $beforeFirstYield = true; // 迭代指针是否是第一个

public function __construct($taskId, Generator $coroutine) {
$this->taskId = $taskId;
$this->coroutine = $coroutine;
}

public function getTaskId() {
return $this->taskId;
}

/**
* 设置插入数据
* @param $sendValue
*/
public function setSendValue($sendValue) {
$this->sendValue = $sendValue;
}

/**
* send数据进行迭代
* @return mixed
*/
public function run() {
// 如果是
if ($this->beforeFirstYield) {
$this->beforeFirstYield = false;
var_dump($this->coroutine->current());
return $this->coroutine->current();
} else {
$retval = $this->coroutine->send($this->sendValue);
$this->sendValue = null;
return $retval;
}
}

/**
* 是否完成
* @return bool
*/
public function isFinished() {
return !$this->coroutine->valid();
}
}

这个封装类,可以更好地去调用运行生成器函数,但只有这个也是不够的,我们还需要一个调度任务类,来代替前面的while:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php
/**
* 任务调度
* Class Scheduler
*/
class Schedular {
protected $maxTaskId = 0; // 任务id
protected $taskMap = []; // taskId => task
protected $taskQueue; // 任务队列

public function __construct()
{
$this->taskQueue = new SplQueue();
}

public function newTask(Generator $coroutine) {
$tid = ++$this->maxTaskId;
// 新增任务
$task = new Task($tid, $coroutine);
$this->taskMap[$tid] = $task;
$this->schedule($task);
return $tid;
}

/**
* 任务入列
* @param Task $task
*/
public function schedule(Task $task) {
$this->taskQueue->enqueue($task);
}

public function run() {
while (!$this->taskQueue->isEmpty()) {
// 任务出列进行遍历生成器数据
$task = $this->taskQueue->dequeue();
$task->run();

if ($task->isFinished()) {
// 完成则删除该任务
unset($this->taskMap[$task->getTaskId()]);
} else {
// 继续入列
$this->schedule($task);
}
}
}
}

很好,我们已经有了一个调取类,还有了一个任务类,可以继续实现上面的功能了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
function task1() {
for ($i = 0; $i <= 300; $i++) {
// 写入文件,大概要3000微妙
usleep(3000);
echo "写入文件{$i}\n";
yield $i;
}
}
function task2() {
for ($i = 0; $i <= 500; $i++) {
// 发送邮件给500名会员,大概要3000微妙
usleep(3000);
echo "发送邮件{$i}\n";
yield $i;
}
}
function task3() {
for ($i = 0; $i <= 100; $i++) {
// 模拟插入100条数据,大概3000微妙
usleep(3000);
echo "插入数据{$i}\n";
yield $i;
}
}
$scheduler = new Scheduler();

$scheduler->newTask(task1());
$scheduler->newTask(task2());
$scheduler->newTask(task3());

$scheduler->run();

除了上面的2个类,task函数和代码1不同的地方,就是多了个yield,那我们试着运行一下:
yield

很好,我们已经实现了可以调用调度任务,进行任务交叉运行的功能了,这就是”协程“,协程可以将多个不同的任务交叉运行。

二、协程与调度器的通信

我们在上面已经实现了一个协程封装了,但是任务和调度器缺少了通信,我们可以重新封装下,使协程当中能够获取当前的任务id,新增任务,以及杀死任务。

先封装一下调用的封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class YieldCall
{
protected $callback;

public function __construct(callable $callback)
{
$this->callback = $callback;
}

/**
* 调用时将返回结果
* @param Task $task
* @param Scheduler $scheduler
* @return mixed
*/
public function __invoke(Task $task, Scheduler $scheduler)
{
// TODO: Implement __invoke() method.
$callback = $this->callback;
return $callback($task, $scheduler);
}
}

同时我们需要小小的改动下调取器的run方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
public function run() {
while (!$this->taskQueue->isEmpty()) {
// 任务出列进行遍历生成器数据
$task = $this->taskQueue->dequeue();
$retval = $task->run();

// 如果返回的是YieldCall实例,则先执行
if ($retval instanceof YieldCall) {
$retval($task, $this);
continue;
}
if ($task->isFinished()) {
// 完成则删除该任务
unset($this->taskMap[$task->getTaskId()]);
} else {
// 继续入列
$this->schedule($task);
}
}
}

新增 getTaskId 函数去返回task_id:

1
2
3
4
5
6
7
8
9
10
11
<?php
function getTaskId() {
// 返回一个YieldCall的实例
return new YieldCall(
// 该匿名函数会先获取任务id,然后send给生成器,并且由YieldCall将task_id返回给生成器函数
function (Task $task, Scheduler $scheduler) {
$task->setSendValue($task->getTaskId());
$scheduler->schedule($task);
}
);
}

然后,我们修改下task1,task2,task3函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
function task1() {
$task_id = (yield getTaskId());
for ($i = 0; $i <= 300; $i++) {
// 写入文件,大概要3000微妙
usleep(3000);
echo "任务{$task_id}写入文件{$i}\n";
yield $i;
}
}
function task2() {
$task_id = (yield getTaskId());
for ($i = 0; $i <= 500; $i++) {
// 发送邮件给500名会员,大概要3000微妙
usleep(3000);
echo "任务{$task_id}发送邮件{$i}\n";
yield $i;
}
}
function task3() {
$task_id = (yield getTaskId());
for ($i = 0; $i <= 100; $i++) {
// 模拟插入100条数据,大概3000微妙
usleep(3000);
echo "任务{$task_id}插入数据{$i}\n";
yield $i;
}
}
$scheduler = new Scheduler();

$scheduler->newTask(task1());
$scheduler->newTask(task2());
$scheduler->newTask(task3());

$scheduler->run();

执行结果:
yield

这样的话,当第一个执行的时候,会先调用getTaskId将task_id返回,然后将任务继续执行,这样,我们就获取到了调度器分配给任务的task_id,是不是很神奇?

三、生成新任务以及杀死任务

现在新增了一个需求:当发送邮件给会员时,需要新增一个发送短信的子任务,当会员id大于200时,则停止(别问我为什么要这样做,我自己都不知道)。

同时,我们可以利用YieldCall,去新增任务和杀死任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
/**
* 传入一个生成器函数用于新增任务给调取器调用
* @param Generator $coroutine
* @return YieldCall
*/
function newTask(Generator $coroutine) {
return new YieldCall(
// 该匿名函数,会在调度器中新增一个任务
function (Task $task, Scheduler $scheduler) use ($coroutine) {
$task->setSendValue($scheduler->newTask($coroutine));
$scheduler->schedule($task);
}
);
}
function killTask($taskId) {
return new YieldCall(
function (Task $task, Scheduler $scheduler) use ($taskId) {
$task->setSendValue($scheduler->killTask($taskId));
$scheduler->schedule($task);
}
);
}

同时,调度器也得有killTask的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
/**
* 杀死一个任务
* @param $taskId
* @return bool
*/
public function killTask($taskId)
{
if (!isset($this->taskMap[$taskId])) {
return false;
}
unset($this->taskMap[$taskId]);

/**
* 遍历队列,找出id相同的则删除
*/
foreach ($this->taskQueue as $i => $task) {
if ($task->getTaskId() === $taskId) {
unset($this->taskQueue[$i]);
break;
}
}
return true;
}

有了新增和删除,我们就可以重新写一下task2以及新增task4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function task2() {
$task_id = (yield getTaskId());
$child_task_id = (yield newTask(task4()));
for ($i = 0; $i <= 500; $i++) {
// 发送邮件给500名会员,大概要3000微妙
usleep(3000);
echo "任务{$task_id}发送邮件{$i}\n";
yield $i;
if ($i == 200) {
yield killTask($child_task_id);
}
}
}
function task4() {
$task_id = (yield getTaskId());
while (true) {
echo "任务{$task_id}发送短信\n";
yield;
}
}

运行结果:
yield

这样,我们就完美的实现了新增任务,以及杀死任务了。

总结

前面所说的,协程只是一种编程逻辑,一种写代码的技巧,协程能够帮助我们更好的切换代码中任务。从上面的例子不难发现,其实协程实现封装较为麻烦,并且不用协程也能实现这些功能,那为什么要用协程呢?
因为协程可以让代码更加的简洁,任务相互之间独立区分开,可以使代码更加的清爽,协程让我们可以更好的控制切换任务流。
前面介绍了那么多,或许有很多人感觉不对,会说“协程不能提升效率吗?”,“协程到底用来干什么的?”
或许由上面的例子很难看出协程的用户,那我们继续举例子吧:
js ajax是phper都了解的一个技术,当点击一个按钮时,先将点击事件ajax传输给后端进行增加一条点击数据,然后出现一个动画,这是一个很正常的事,那么请问,如果ajax是同步,并且在网络不好的情况,会发生什么呢?没错,点击之后,页面会卡几秒(网络不好),请求完毕之后,才会出现一个动画。

协程的用处就在这了,我们可以利用协程,把一些同步io等待的代码逻辑,改为异步,在等待的时间内,可以让cpu去处理其他任务。就如同小学时候做的一道题:
小明烧开水需要10分钟,刷牙需要3分钟,吃早餐需要5分钟,请问做完这些事情总共需要多少分钟?答案是10分钟,因为在烧开水这个步骤时,不需要坐在那里看水壶(异步,io耗时)可以先去刷牙,然后去吃早餐。

以上就是php yield关于协程的全部内容了。

swoole

由总结可以看出,协程用在最多的应用场景,在于需要io耗时,cpu可以节省出来的场景,并且必须是异步操作,这里推荐swoole扩展:https://www.swoole.com

Swoole-4.1.0 正式版发布,主要改动及新特性:

  • PHP 原生Redis、PDO、MySQLi轻松协程化,使用Swoole\Runtime::enableCoroutine()即可将普通的同步阻塞Redis、PDO、MySQL操作变为协程调度的异步非阻塞IO

  • 协程跟踪功能:新增两个方法 Coroutine::listCoroutine() 可遍历当前所有协程,Coroutine::getBackTrace($cid)可获取某个协程的函数调用栈

  • 支持在协程和Server中exit,此时将抛出可捕获的\Swoole\ExitException异常

  • 移除所有迭代器(table/connection/coroutine_list)的PCRE依赖机制
  • 新增http_compression配置项,底层会自动判断客户端传入的Accept-Encoding选择合适的压缩方法,支持GoogleBrotli压缩
  • 重构了底层Channel和协程Http客户端的C代码为C++协程模式,解决历史遗留的异步时序问题,稳定性大大提升
  • 更完整稳定的HTTP2支持和SSL处理
  • 增加open_websocket_close_frame配置,可以在onMessage时间中接收close帧

具体更新内容文档:https://wiki.swoole.com/wiki/page/966.html

easyswoole

EasySwoole 是一款基于Swoole Server 开发的常驻内存型的分布式框架,专为API而生,摆脱传统PHP运行模式在进程唤起和文件加载上带来的性能损失。EasySwoole 高度封装了 Swoole Server而依旧维持 Swoole Server 原有特性,支持同时混合监听HTTP、自定义TCP、UDP协议,让开发者以最低的学习成本和精力编写多进程,可异步,高可用的应用服务。

官网:http://www.easyswoole.com

本文为转来自www.php20.cn。

文章目录
  1. 1. php yield关键字以及协程的实现
    1. 1.1. Iterator接口(迭代器接口)
    2. 1.2. 生成器
      1. 1.2.1. 一、生成器方法
      2. 1.2.2. 二、语法
        1. 1.2.2.1. 1、颠覆常识的yield
        2. 1.2.2.2. 2、yield的其他语法
      3. 1.2.3. 三、特性总结
    3. 1.3. 协程
      1. 1.3.1. 一、实现一个简单的协程
      2. 1.3.2. 二、协程与调度器的通信
      3. 1.3.3. 三、生成新任务以及杀死任务
    4. 1.4. 总结
    5. 1.5. swoole
    6. 1.6. easyswoole
本站总访问量 | 本页面被访问 | 您是第位小伙伴

© XueSi博客 版权所有 备案号 : 赣ICP备19008485号-1