前言
第一部分: 半协程调度器
- 统一生成器接口
- 生成器迭代
- 生成器返回值
- 生成器委托
- 改写 return
- 抽象异步模型
- 引入异常处理
- 异常: 嵌套任务透传
- 异常: 传递流程
- 异常: 重新进行 CPS 变换
- 异常: 重新加入 Async
- Syscall 与 Context
- 调度器: 里程碑
- spawn
- callcc
- race 与 timeout
- all 与 parallel
- channel 与协程间通信
- 无缓存 channel
- 缓存 channel
- channel 演示
- FutureTask 与 fork
第二部分: Koa
- 穿越地心之旅
- 洋葱圈模型
- rightReduce与中间件compose
- Koa::Application
- Koa::Context
- Koa::Request
- Koa::Response
- Koa - HelloWorld
- Middleware Interface
- Middleware: 全局异常处理
- Middleware: Router
- Middleware: 请求超时
- 一个综合示例
附录
参考
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
channel 演示
channel演示
这是我们最终得到的接口:
<?php
function go(...$args)
{
spawn(...$args);
}
function chan($n = 0)
{
if ($n === 0) {
return new Channel();
} else {
return new BufferChannel($n);
}
}
第一个典型例子, PINGPONG。
与golang的channel类似,我们可以在两个semicoroutine之间做同步:
<?php
// 构建两个单向channel, 我们只单向收发数据
$pingCh = chan();
$pongCh = chan();
go(function() use($pingCh, $pongCh) {
while (true) {
echo (yield $pingCh->recv());
yield $pongCh->send("PONG\n");
// 递归调度器实现,需要引入异步的方法退栈,否则Stack Overflow...
// 或者考虑将send或者recv以defer方式实现
yield async_sleep(1);
}
});
go(function() use($pingCh, $pongCh) {
while (true) {
echo (yield $pongCh->recv());
yield $pingCh->send("PING\n");
yield async_sleep(1);
}
});
// start up
go(function() use($pingCh) {
echo "start up\n";
yield $pingCh->send("PING");
});
// output:
/*
start up
PING
PONG
PING
PONG
PING
PONG
PING
...
*/
当然,我们可以很轻易构建一个生产者-消费者模型:
<?php
$ch = chan();
// 生产者1
go(function() use($ch) {
while (true) {
yield $ch->send("producer 1");
yield async_sleep(1000);
}
});
// 生产者2
go(function() use($ch) {
while (true) {
yield $ch->send("producer 2");
yield async_sleep(1000);
}
});
// 消费者1
go(function() use($ch) {
while (true) {
$recv = (yield $ch->recv());
echo "consumer1: $recv\n";
}
});
// 消费者2
go(function() use($ch) {
while (true) {
$recv = (yield $ch->recv());
echo "consumer2: $recv\n";
}
});
// output:
/*
consumer1 recv from producer 1
consumer1 recv from producer 2
consumer1 recv from producer 1
consumer2 recv from producer 2
consumer1 recv from producer 2
consumer2 recv from producer 1
consumer1 recv from producer 1
consumer2 recv from producer 2
consumer1 recv from producer 2
consumer2 recv from producer 1
consumer1 recv from producer 1
consumer2 recv from producer 2
......
*/
channel 自身是first-class value
, 所以可传递:
<?php
// 我们通过一个chan来发送另一个chan
// 然后等待接收到这个chan的semicoroutine回送数据
$ch = chan();
go(function() use ($ch) {
$anotherCh = chan();
yield $ch->send($anotherCh);
echo "send another channel\n";
yield $anotherCh->send("HELLO");
echo "send hello through another channel\n";
});
go(function() use($ch) {
/** @var Channel $anotherCh */
$anotherCh = (yield $ch->recv());
echo "recv another channel\n";
$val = (yield $anotherCh->recv());
echo $val, "\n";
});
// output:
/*
send another channel
recv another channel
send hello through another channel
HELLO
*/
我们通过控制channel缓存大小 观察输出结果:
<?php
$ch = chan($n);
go(function() use($ch) {
$recv = (yield $ch->recv());
echo "recv $recv\n";
$recv = (yield $ch->recv());
echo "recv $recv\n";
$recv = (yield $ch->recv());
echo "recv $recv\n";
$recv = (yield $ch->recv());
echo "recv $recv\n";
});
go(function() use($ch) {
yield $ch->send(1);
echo "send 1\n";
yield $ch->send(2);
echo "send 2\n";
yield $ch->send(3);
echo "send 3\n";
yield $ch->send(4);
echo "send 4\n";
});
// $n = 1;
// output:
/*
send 1
recv 1
send 2
recv 2
send 3
recv 3
send 4
recv 4
*/
// $n = 2;
// output:
/*
send 1
send 2
recv 1
recv 2
send 3
send 4
recv 3
recv 4
*/
// $n = 3;
// output:
/*
send 1
send 2
send 3
recv 1
recv 2
recv 3
send 4
recv 4
*/
一个更具体的生产者消费者的例子:
<?php
// 缓存两个结果
$ch = chan(2);
// 从channel接口请求写过写文件
go(function() use($ch) {
$file = new AsyncFile("path/to/save");
while (true) {
list($host, $status) = (yield $ch->recv());
yield $file->write("$host: $status\n");
}
});
// 请求并写入chan
go(function() use($ch) {
while (true) {
$host = "www.baidu.com";
$resp = (yield async_curl_get($host));
yield $ch->send([$host, $resp->statusCode]);
}
});
// 请求并写入chan
go(function() use($ch) {
while (true) {
$host = "www.bing.com";
$resp = (yield async_curl_get($host));
yield $ch->send([$host, $resp->statusCode]);
}
});
// output:
channel的发送与接受没有超时机制,Golang可以select多个chan实现超时处理,我们可以做一个select设施,或者在send于recv接受直接添加超时参数,扩展接口功能,留待读者自行实现。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论