本文实例讲述了php基于协程实现异步的方法。分享给大家供大家参考,具体如下:
github上php的协程大部分是根据这篇文章实现的:http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html。
它们最终的结果都是把回调变成了优雅的顺序执行的代码,但还是阻塞的,不是真正的异步。
比如最热门的:https://github.com/recoilphp/recoil
先安装:
1
|
composer require recoil /recoil
|
执行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<?php
//recoil.php
include __DIR__ . '/vendor/autoload.php' ;
use Recoil\React\ReactKernel;
$i = 100000;
ReactKernel::start(task1());
ReactKernel::start(task2());
function task1(){
global $i ;
echo "wait start" . PHP_EOL;
while ( $i -- > 0) {
yield;
}
echo "wait end" . PHP_EOL;
};
function task2(){
echo "Hello " . PHP_EOL;
yield;
echo "world!" . PHP_EOL;
}
|
结果:
wait start
//等待若干秒
wait end
Hello
world!
我本来是想让两个任务并行,结果两个任务变成了串行,中间等待的时间什么事情都干不了。React响应式的编程是严格禁止这种等待的,所以我就参照unity3d的协程自己写了个php版本的。上代码:
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
<?php
//Coroutine.php
//依赖swoole实现的定时器,也可以用其它方法实现定时器
class Coroutine
{
//可以根据需要更改定时器间隔,单位ms
const TICK_INTERVAL = 1;
private $routineList ;
private $tickId = -1;
public function __construct()
{
$this ->routineList = [];
}
public function start(Generator $routine )
{
$task = new Task( $routine );
$this ->routineList[] = $task ;
$this ->startTick();
}
public function stop(Generator $routine )
{
foreach ( $this ->routineList as $k => $task ) {
if ( $task ->getRoutine() == $routine ){
unset( $this ->routineList[ $k ]);
}
}
}
private function startTick()
{
swoole_timer_tick(self::TICK_INTERVAL, function ( $timerId ){
$this ->tickId = $timerId ;
$this ->run();
});
}
private function stopTick()
{
if ( $this ->tickId >= 0) {
swoole_timer_clear( $this ->tickId);
}
}
private function run()
{
if ( empty ( $this ->routineList)){
$this ->stopTick();
return ;
}
foreach ( $this ->routineList as $k => $task ) {
$task ->run();
if ( $task ->isFinished()){
unset( $this ->routineList[ $k ]);
}
}
}
}
class Task
{
protected $stack ;
protected $routine ;
public function __construct(Generator $routine )
{
$this ->routine = $routine ;
$this ->stack = new SplStack();
}
/**
* [run 协程调度]
* @return [type] [description]
*/
public function run()
{
$routine = & $this ->routine;
try {
if (! $routine ){
return ;
}
$value = $routine ->current();
//嵌套的协程
if ( $value instanceof Generator) {
$this ->stack->push( $routine );
$routine = $value ;
return ;
}
//嵌套的协程返回
if (! $routine ->valid() && ! $this ->stack->isEmpty()) {
$routine = $this ->stack->pop();
}
$routine ->next();
} catch (Exception $e ) {
if ( $this ->stack->isEmpty()) {
/*
throw the exception
*/
return ;
}
}
}
/**
* [isFinished 判断该task是否完成]
* @return boolean [description]
*/
public function isFinished()
{
return $this ->stack->isEmpty() && ! $this ->routine->valid();
}
public function getRoutine()
{
return $this ->routine;
}
}
|
测试代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<?php
//test.php
require 'Coroutine.php' ;
$i = 10000;
$c = new Coroutine();
$c ->start(task1());
$c ->start(task2());
function task1(){
global $i ;
echo "wait start" . PHP_EOL;
while ( $i -- > 0) {
yield;
}
echo "wait end" . PHP_EOL;
};
function task2(){
echo "Hello " . PHP_EOL;
yield;
echo "world!" . PHP_EOL;
}
|
结果:
wait start
Hello
world!
//等待几秒,但不阻塞
wait end
希望本文所述对大家PHP程序设计有所帮助。
原文链接:https://blog.csdn.net/llj1985/article/details/51684210