创建
go和create方式创建
use Swoole\Coroutine;
use function Swoole\Coroutine\Run;
// 必须使用run创建协程容器,才能写协程,否则会报错
// 框架中可以直接使用go是因为框架启动的时候已经创建了协程容器
run(function () {
// go方式创建协程,php.ini里开启短命名才可以
// [swoole]
// swoole.use_shortname = on
// Swoole\Coroutine::create等价于go函数
go(function () {
Coroutine::sleep(2); // 必须使用协程的sleep,不然会同步阻塞
echo "通过go创建协程", PHP_EOL;
});
// 最原始的方式创建协程,创建完成后,会返回协程的id
$cid = Coroutine::create(function () {
Coroutine::sleep(2);
echo "通过Coroutine::create创建协程", PHP_EOL;
});
echo "这里是第一个输出的地方", PHP_EOL;
});
echo "这里是最后输出的地方", PHP_EOL;
Scheduler方式创建
use Swoole\Coroutine\Scheduler;
$scheduler = new Scheduler();
$scheduler->set([
'max_coroutine'=>200,
]);
// 添加单个协程
$scheduler->add(function ($a,$b){
// 逻辑
},'参数a','参数b');
// 创建多个协程
$scheduler->parallel(2, function () {
// 同样的协程创建两个
});
$scheduler->start();// 执行协程
echo "最后执行这里";
batch方式创建
parallel方式创建
通信
上述例子中的两个协程没有通信,如果要通信,可以用channel进行通信
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
use function Swoole\Coroutine\Run;
run(function () {
$channel = new Channel(1);
Coroutine::create(function () use ($channel) {
for ($i = 0; $i < 10; $i++) {
echo "生产消息的协程{$i}", PHP_EOL;
Coroutine::sleep(1.0);
$channel->push([
'rand' => rand(1000, 9999),
'index' => $i,
]);
}
});
Coroutine::create(function () use ($channel) {
while (1) {
// 应该会阻塞在这里
$data = $channel->pop(1);
if ($data) {
echo "接收消息的协程";
print_r($data);
} else {
echo $channel->errCode === SWOOLE_CHANNEL_TIMEOUT;
break;
}
}
});
});
我们还可以通过waitGroup同步阻塞
use Swoole\Coroutine;
use Swoole\Coroutine\WaitGroup;
use function Swoole\Coroutine\Run;
run(function () {
$wg = new WaitGroup();
$result = [];// 共享内存
$wg->add();
Coroutine::create(function () use ($wg, &$result) {
Coroutine::sleep(1.0);
$result[] = 1;
$wg->done();
});
$wg->add();
Coroutine::create(function () use ($wg, &$result) {
Coroutine::sleep(1.0);
$result[] = 2;
$wg->done();
});
$wg->wait(); // 如果计数不为0,则会阻塞这里
print_r($result);// 可以得到两个协程的数据
});
还可以通过Barrier来简化waitGroup,不需要add和done操作
use Swoole\Coroutine;
use Swoole\Coroutine\Barrier;
use function Swoole\Coroutine\Run;
run(function () {
$barrier = Barrier::make();
$result = []; // 共享内存
// 该use还是得use,即使可以不使用
Coroutine::create(function () use ($barrier,&$result) {
Coroutine::sleep(1.0);
$result[] = 1;
});
Coroutine::create(function () use ($barrier, &$result) {
Coroutine::sleep(1.0);
$result[] = 2;
});
Barrier::wait($barrier); // 如果计数不为0,则会阻塞这里
print_r($result); // 可以得到两个协程的数据
});
异常处理
协程之间是互不干扰的,但是有个协程报错如果不做处理导致程序退出的,会影响到其它协程。
use Swoole\Coroutine;
use function Swoole\Coroutine\Run;
run(function () {
// 协程1
Coroutine::create(function () {
a(); // 调用不存在的协程,导出php进程中断,协程2还没执行完成就被退出了
});
Coroutine::create(function (){
Coroutine::sleep(1);
echo "协程2";
});
});
协程挂起
协程可以执行到一半的时候,挂起,等待别的协程去恢复它执行。
当协程执行到yield的是,协程会挂起,然后使用resume方法去恢复它的执行。
我们可以用defer方法,执行到协程容器末尾的时候,最后执行
我们可以通过listCoroutine方法获取所有的协程;