请问php推出的协程是什么?
网友回复
你可能已经听过『进程』和『线程』这两个概念。
进程就是二进制可执行文件在计算机内存里的一个运行实例,就好比你的.exe文件是个类,进程就是new出来的那个实例。
进程是计算机系统进行资源分配和调度的基本单位(调度单位这里别纠结线程进程的),每个CPU下同一时刻只能处理一个进程。
所谓的并行,只不过是看起来并行,CPU事实上在用很快的速度切换不同的进程。
进程的切换需要进行系统调用,CPU要保存当前进程的各个信息,同时还会使CPUCache被废掉。
所以进程切换不到非不得已就不做。
那么怎么实现『进程切换不到非不得已就不做』呢?
首先进程被切换的条件是:进程执行完毕、分配给进程的CPU时间片结束,系统发生中断需要处理,或者进程等待必要的资源(进程阻塞)等。你想下,前面几种情况自然没有什么话可说,但是如果是在阻塞等待,是不是就浪费了。
其实阻塞的话我们的程序还有其他可执行的地方可以执行,不一定要傻傻的等!
所以就有了线程。
线程简单理解就是一个『微进程』,专门跑一个函数(逻辑流)。
所以我们就可以在编写程序的过程中将可以同时运行的函数用线程来体现了。
线程有两种类型,一种是由内核来管理和调度。
我们说,只要涉及需要内核参与管理调度的,代价都是很大的。这种线程其实也就解决了当一个进程中,某个正在执行的线程遇到阻塞,我们可以调度另外一个可运行的线程来跑,但是还是在同一个进程里,所以没有了进程切换。
还有另外一种线程,他的调度是由程序员自己写程序来管理的,对内核来说不可见。这种线程叫做『用户空间线程』。
协程可以理解就是一种用户空间线程。
协程,有几个特点:
协同,因为是由程序员自己写的调度策略,其通过协作而不是抢占来进行切换
在用户态完成创建,切换和销毁
从编程角度上看,协程的思想本质上就是控制流的主动让出(yield)和恢复(resume)机制
generator经常用来实现协程
说到这里,你应该明白协程的基本概念了吧?
我们用一个代码示例来讲解一下:
<?php在这个代码中,我们主要做了 3 件事:写入文件、发送邮件、及插入数据。
function task1()
{
for ($i = 0; $i <= 300; $i++) {
// 写入文件,大概要 3000 微秒
usleep(3000);
echo "写入文件{$i}\n";
}
}
function task2()
{
for ($i = 0; $i <= 500; $i++) {
// 发送邮件给 500 名会员,大概 3000 微秒
usleep(3000);
echo "发送邮件{$i}\n";
}
}
function task3()
{
for ($i = 0; $i <= 100; $i++) {
// 模拟插入 100 条数据,大概 3000 微秒
usleep(3000);
echo "插入数据{$i}\n";
}
}
task1();
task2();
task3();
再看下面这段代码:
<?php这段代码也是做了 3 件事,写入文件、发送邮件和插入数据。但是和上面的不同的是,这段代码将这 3 件事交叉执行,每个任务执行完一次之后,切换到另一个任务,如此循环。类似于这样的执行顺序,就是协程。
function task1($i)
{
// 使用 $i 标识 写入文件,,大概要3000微秒
if ($i > 300) {
return false;// 超过 300 不用写了
}
echo "写入文件{$i}\n";
usleep(3000);
return true;
}
function task2($i)
{
// 使用 $i 标识 发送邮件,大概要 3000 微秒
if ($i > 500) {
return false;// 超过 500 不用发送了
}
echo "发送邮件{$i}\n";
usleep(3000);
return true;
}
function task3($i)
{
// 使用 $i 标识 插入数据,大概要 3000 微秒
if ($i > 100) {
return false;// 超过 100 不用插入
}
echo "插入数据{$i}\n";
usleep(3000);
return true;
}
$i = 0;
$task1Result = true;
$task2Result = true;
$task3Result = true;
while (true) {
$task1Result && $task1Result = task1($i);
$task2Result && $task2Result = task2($i);
$task3Result && $task3Result = task3($i);
if ($task1Result === false && $task2Result === false && $task3Result === false) {
break;// 全部任务完成,退出循环
}
$i++;
}
协程是指一种用代码实现任务交叉执行的逻辑,协程可以使得代码 1 中的 3 个函数交叉运行,在实现了协程的框架中,我们不需要通过代码 2 的方法实现任务交叉执行。直接可让代码 1 中的 while(1),执行一次后切换。
在 php 中,实现协程主要使用 2 种方式:
yield 生成器实现 (详细原理可查看 http://www.php20.cn/article/148)
swoole 扩展实现
swoole 实现协程代码:
<?php
function task1()
{
for ($i = 0; $i <= 300; $i++) {
// 写入文件,大概要 3000 微秒
usleep(3000);
echo "写入文件{$i}\n";
Co::sleep(0.001);// 挂起当前协程,0.001 秒后恢复 // 相当于切换协程
}
}
function task2()
{
for ($i = 0; $i <= 500; $i++) {
// 发送邮件给 500 名会员,大概 3000 微秒
usleep(3000);
echo "发送邮件{$i}\n";
Co::sleep(0.001);// 挂起当前协程,0.001 秒后恢复 // 相当于切换协程
}
}
function task3()
{
for ($i = 0; $i <= 100; $i++) {
// 模拟插入 100 条数据,大概 3000 微秒
usleep(3000);
echo "插入数据{$i}\n";
Co::sleep(0.001);// 挂起当前协程,0.001 秒后恢复 // 相当于切换协程
}
}
$pid1 = go('task1');// go 函数是 swoole 的开启协程函数,用于开启一个协程
$pid2 = go('task2');
$pid3 = go('task3');
以上代码,即可实现切换函数。
为什么要用 sleep 挂起协程实现切换呢?因为 swoole 的协程是自动的,当协程内遇上 I/O 操作 (mysql、redis) 等时,swoole 的协程会自动切换,运行到下一个协程任务中 (切换后,I/O继续执行),直到下一个协程任务完成或者被切换 (遇上 I/O),如此反复,直到所有协程任务完成,则任务完成。
协程与进程
由上面的 协程执行顺序 中的代码 2,我们很容易发现,协程其实只是运行在一个进程中的函数,只是这个函数会被切换到下一个执行,可以这么说:
协程只是一串运行在进程中的任务代码,只是这些任务代码可以交叉运行。 注意,协程并不是多任务并行,属于多任务串行,每个进程在一个时间只执行了一个任务。
- 如何使用Grafana搭建一个监控仪表盘系统?
- 如何使用chatgpt直接调用midjourney或dall-2的api生成图片?
- 如何通过文字来识别用户需要绘画?
- 抖音字节小程序的虚拟商品订单如何使用php进行同步?
- 如何对html静态网站页面的html、css、js代码进行加密混淆?
- colorui radio标签为啥在小程序中会错位失效?
- mysql数据库被勒索病毒删除,如何通过mysqlbinlog恢复数据库?
- win10电脑的显存多少如何查看?
- 有没有类似chatgpt的开源中文对话聊天机器人模型?
- uniapp、小程序、h5的page页面onload方法如何在onLaunch执行完后再触发?