- 优先执行子协程(即go()里面的逻辑),直到发生协程yield(co::sleep 处),然后协程调度到外层协程
- 每个协程都是相互独立的,需要创建单独的内存空间 (栈内存);协程退出时会释放申请的stack内存;协程栈内存占用超过后ZendVM会自动扩容。
协程基础使用
use Swoole\Coroutine;
use function Swoole\Coroutine\go;
use function Swoole\Coroutine\run;
// 协程容器,协程代码需要在协程容器中使用
run(function(){
// 创建协程时传参
go(function($val1, $val2, $val3){
echo $val1;
// 创建协程时不传参
go(function(){
// 遍历当前进程内的所有协程,元素为协程id
foreach(Coroutine::list() as $cid) {
// 获取指定协程的运行时间,便于分析统计、找出僵尸协程,默认当前协程;毫秒精度
var_dump('协程'.$cid.'的运行时间:'.Coroutine::getElapsed($cid));
// 获取当前PHP栈的内存使用量,默认当前协程,版本4.8.0以上可用
// var_dump('协程'.$cid.'的内存使用量:'.Coroutine::getStackUsage($cid));
// 获取协程函数的调用栈
var_dump(Coroutine::getBackTrace($cid));
}
// 获取协程状态
var_dump(Coroutine::stats());
[
'event_num' => '当前reactor事件数量',
'signal_listener_num' => '当前监听信号的数量',
'aio_task_num' => '异步IO任务数量 (这里的aio指文件IO或dns, 不包含其它网络IO, 下同)',
'aio_worker_num' => '异步IO工作线程数量',
'c_stack_size' => '每个协程的C栈大小',
'coroutine_num' => '当前运行的协程数量',
'coroutine_peak_num' => '当前运行的协程数量的峰值',
'coroutine_last_cid' => '最后创建协程的 id',
];
Coroutine::sleep(2.0);
echo 100;
// 获取指定协程的上下文对象,默认获取当前协程的
var_dump(Coroutine::getContext(Coroutine::getCid()));
});
echo $val2;
echo $val3;
}, 1, 2, 3);
$a = 100;
// 用于释放资源,会在协程关闭之前 (即协程函数执行完毕时) 进行调用,就算抛出了异常,已注册的defer也会被执行。
// 调用顺序是逆序的(先进后出, 也就是先注册defer的后执行,先进后出
// 示例:例如关联数据库连接
Coroutine::defer(function () use ($a) {
// 获取当前协程的唯一ID,进程内唯一的正整数
var_dump($cid = Coroutine::getCid());
// 获取指定协程的父ID,不传$cid时获取当前协程的父ID;没有父协程时返回false,在非嵌套协程中使用返回-1
var_dump($pcid = Coroutine::getPcid($cid));
// 判断指定协程是否存在
var_dump(Coroutine::exists($cid));
var_dump(Coroutine::exists($pcid));
unset($a);
});
});
协程上下文对象
- 协程退出后上下文自动清理 (如无其它协程或全局变量引用)
- 不需要defer注册和调用,省去开销
- 无PHP数组实现的上下文的哈希计算开销 (在协程数量巨大时有一定好处)
- Context使用ArrayObject, 满足各种存储需求(既是对象,也可以以数组方式操作)
- 不需要手动销毁变量
use Swoole\Coroutine;
use function Swoole\Coroutine\go;
use function Swoole\Coroutine\run;
use Swoole\Coroutine\Context;
/**
* 获取对象在当前进程内的唯一ID
* @param object|Resource $object
* @return int
*/
function php_object_id($object)
{
static $id = 0;
static $map = [];
$hash = spl_object_hash($object);
return $map[$hash] ?? ($map[$hash] = ++$id);
}
class A
{
public function __construct()
{
echo __CLASS__ . '#' . php_object_id((object)$this) . ' constructed' . PHP_EOL;
}
public function __destruct()
{
echo __CLASS__ . '#' . php_object_id((object)$this) . ' destructed' . PHP_EOL;
}
}
run(function(){
go(function(){
$context = Coroutine::getContext();
$context['resource1'] = new A;
$context['resource2'] = new A;
$context['resource3'] = new A;
var_dump($context instanceof Context); // true
var_dump($context instanceof ArrayObject); // true
var_dump(Coroutine::getContext());
go(function(){
Coroutine::getContext()['resource4'] = new A;
Coroutine::getContext()['resource5'] = new A;
var_dump(Coroutine::getContext());
});
});
});
并发协程调用
版本4.8.0以上可用
use Swoole\Coroutine;
use function Swoole\Coroutine\go;
use function Swoole\Coroutine\run;
run(function () {
// join的第一个参数时协程CID数组
$status = Coroutine::join([
go(function () use (&$result) {
$result['baidu'] = strlen(file_get_contents('https://www.baidu.com/'));
}),
go(function () use (&$result) {
$result['google'] = strlen(file_get_contents('https://www.google.com/'));
})
], 1); // 1为总超时时间,超时后会立即返回。但正在运行的协程会继续执行完毕,而不会中止
var_dump($result, $status, swoole_strerror(swoole_last_error(), 9));
});
版本4.5.2以上可用
use Swoole\Coroutine;
use function Swoole\Coroutine\batch;
Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]);
$start_time = microtime(true);
Coroutine\run(function () {
$use = microtime(true);
// 第一个参数为回调方法的数组,可以指定$key,指定时返回值也是以$key为键,函数返回为值;第二个参数为总超时时间,超时后会立即返回。但正在运行的协程会继续执行完毕,而不会中止
$results = batch([
'file_put_contents' => function () {
return file_put_contents(__DIR__ . '/greeter.txt', "Hello,Swoole.");
},
'gethostbyname' => function () {
return gethostbyname('localhost');
},
'file_get_contents' => function () {
return file_get_contents(__DIR__ . '/greeter.txt');
},
'sleep' => function () {
sleep(1);
return true; // 返回NULL 因为超过了设置的超时时间0.1秒,超时后会立即返回。但正在运行的协程会继续执行完毕,而不会中止。
},
'usleep' => function () {
usleep(1000);
return true;
},
], 0.1);
$use = microtime(true) - $use;
echo "Use {$use}s, Result:\n";
var_dump($results);
});
$end_time = microtime(true) - $start_time;
echo "Use {$end_time}s, Done\n";
版本4.5.5以上可用
use Swoole\Coroutine;
use Swoole\Coroutine\System;
use function Swoole\Coroutine\parallel;
$start_time = microtime(true);
Coroutine\run(function () {
$use = microtime(true);
$results = [];
// 使用$n个协程指定方法
parallel(2, function () use (&$results) {
System::sleep(0.2);
$results[] = System::gethostbyname('localhost');
});
$use = microtime(true) - $use;
echo "Use {$use}s, Result:\n";
var_dump($results);
});
$end_time = microtime(true) - $start_time;
echo "Use {$end_time}s, Done\n";