PHP协程的概念是什么,用代码怎样说明
Admin 2022-08-16 群英技术资讯 528 次浏览
“协程”就是用户态的线程
要理解是什么是“用户态的线程”,必然就要先理解什么是“内核态的线程”。 内核态的线程是由操作系统来进行调度的,在切换线程上下文时,要先保存上一个线程的上下文,然后执行下一个线程,当条件满足时,切换回上一个线程,并恢复上下文。 协程也是如此,只不过,用户态的线程不是由操作系统来调度的,而是由程序员来调度的,是在用户态的 -- 摘自链接描述
我们有两个函数 task1
,task2
,我们来手动调度它们的执行顺序,比如在task1
执行一半的时候去执行task2
,两个或者多个函数之间交替执行(这就是协程
的概念)。
我们来个正常的函数调用方式:
<?php
function task1()
{
echo "task1函数 执行1\n";
echo "task1函数 执行2\n";
}
function task2()
{
echo "task2函数 执行1\n";
echo "task2函数 执行2\n";
}
// 调度
task1();
task2();
可想而知,以上的输出肯定是:
```task1函数 执行第1 task1函数 执行第2 task2函数 执行第1 task2函数 执行第2 ```但是我想在程序输出task1函数 执行1
之后就输出task2函数 执行1
怎么办?
这个时候 yield 就派上用场了,PHP
里的协程是需要借助 yield 来完成的。记住,yield 不是协程,而是协程
需要借助 yield 的特性来实现。
<?php
function task1()
{
echo "task1函数 执行1\n";
yield;
echo "task1函数 执行2\n";
}
function task2()
{
echo "task2函数 执行1\n";
yield;
echo "task2函数 执行2\n";
}
// 调度
$task1 = task1(); // 返回一个生成器
$task2 = task2(); // 返回一个生成器
$task1->current();
$task2->current();
以上输出:
```task1函数 执行1 task2函数 执行1 ```很好,以上结果达到了我们的预期。但是怎么让函数里的代码往下执行呢?
调用生成器的next
方法:
$task1->next();
$task2->next();
最后你将看到的输出结果是两个函数交替执行输出的:
```task1函数 执行1 task2函数 执行1 task1函数 执行2 task2函数 执行2 ```以上的代码实现可以抽象出两个概念,任务
和调度
,任务
就是task函数,调度
就是我们怎么去调用这些task函数
上一个小段总结里有两个概念叫任务
和调度
,我们简单的封装个任务生成器和调度器
// 任务生成器
$createTask = (function () {
$tasks = [];
return function ($callback) use (&$tasks) {
$task = [
'task' => $callback(),
'id' => count($tasks) + 1,
];
array_push($tasks, $task);
return $task;
};
})();
// 调度器
function schedule($tasks)
{
$first = [];
while (!empty($tasks)) {
$task = array_shift($tasks);
if (!array_key_exists($task['id'], $first)) {
$first[$task['id']] = true;
$task['task']->current();
} else {
$task['task']->next();
}
if (!$task['task']->valid()) {
unset($tasks[$k]);
} else {
array_push($tasks, $task);
}
}
}
使用
$tasks = [
$createTask(function () {
echo "任务1 执行第1次\n";
yield;
echo "任务1 执行第2次\n";
}),
$createTask(function () {
echo "任务2 执行第1次\n";
yield;
echo "任务2 执行第2次\n";
})
];
schedule($tasks);
输出结果:
任务1 执行第1次
任务2 执行第1次
任务1 执行第2次
任务2 执行第2次
可以从结果看出,调度器已经实现了多个任务之间进行协作。
现在有个需求!就是任务在遇到网络请求的时候,我们无需等待网络请求的响应结果,而是遇到网络请求的时候,把这个任务挂起,然后去执行其它任务,等网络请求收到响应结果了再通知我们处理
这时候需要我们用到非阻塞IO调用
相关技术,涉及到系统内核层面,想了解可以点击链接描述
在PHP里我们需要安装个扩展eio
,大家自行安装
编码:
$tasks = [
$createTask(function () {
echo "任务1 执行第1次\n";
yield;
echo "任务1 执行第2次\n";
}),
$createTask(function () {
echo "任务2 执行第1次\n";
eio_custom(function () {
return file_get_contents('https://segmentfault.com/');
}, EIO_PRI_DEFAULT, function ($data, $ret) {
echo "请求完成\n";
});
yield;
echo "任务2 执行第2次\n";
})
];
schedule($tasks);
eio_event_loop();
在任务2 执行第1次
的时候,遇到网络请求,我们把请求任务交给系统内核,然后切换到其它任务去,等请求任务完成后回调我们传入的函数。
输出结果:
任务1 执行第1次
任务2 执行第1次
任务1 执行第2次
任务2 执行第2次
任务2 执行第1次的请求完成
完!
原文地址:https://segmentfault.com/a/1190000016061073
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
在对用户输入数据进行过滤时通常都是自己写方法进行判断 ,比如验证邮箱的时候使用正则表达式。那么如果不用正则还有其它跟简单的方法吗?
面向对象的三大特点是封装、继承、多态。本文将通过示例详细讲讲这三者的使用,文中示例代码讲解详细,需要的可以参考一下
本篇文章小编给大家分享一下golang防缓存击穿singleflight代码实现方法,文章代码介绍的很详细,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看。
php方法断点的实现:1、使用phpdbg_break_function()来给这个testFunc()方法设置一个断点。2、直接进行两次s单步,可以看到global $i对应的 opcode 操作是 BIND_GLOBAL。
在本篇文章里小编给大家分享了关于PHP将英文数字转换为阿拉伯数字实例内容,有兴趣的朋友们可以参考学习下。
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008