在php中,实现协程主要由两种方式 1.yield生成器的实现 2.swoole扩展实现
在php中,实现协程主要由两种方式
1.yield生成器的实现
2.swoole扩展实现
协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。
一个程序可以包含多个协程,可以对比与一个进程包含多个线程,因而下面我们来比较协程和线程。
我们知道多个线程相对独立,有自己的上下文,切换受系统控制;而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程 由当前协程来控制
首先,我们来看一个原生php代码:
function task1(){
for ($i=0;$i<=300;$i++){
//写入文件,大概要3000微秒
usleep(3000);
echo "写入文件{$i} ";
}
}
function task2(){
for ($i=0;$i<=500;$i++){
//发送邮件给500名会员,大概3000微秒
usleep(3000);
echo "发送邮件{$i} ";
}
}
function task3(){
for ($i=0;$i<=100;$i++){
//模拟插入100条数据,大概3000微秒
usleep(3000);
echo "插入数据{$i} ";
}
}
task1();
task2();
task3();
在这个代码中,我们主要做了3件事:写入文件,发送邮件,以及插入数据. 再看下面这段代码:
function task1($i)
{
//使用$i标识 写入文件,大概要3000微秒
if ($i > 300) {
return false;//超过300不用写了
}
echo "写入文件{$i} ";
usleep(3000);
return true;
}
function task2($i)
{
//使用$i标识 发送邮件,大概要3000微秒
if ($i > 500) {
return false;//超过500不用发送了
}
echo "发送邮件{$i} ";
usleep(3000);
return true;
}
function task3($i)
{
//使用$i标识 插入数据,大概要3000微秒
if ($i > 100) {
return false;//超过100不用插入
}
echo "插入数据{$i} ";
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++;
}
这段代码也是做了3件事,写入文件,发送邮件,以及插入数据,但是和上面的不同的是,这段代码将这3件事交叉执行,每个任务执行完一次之后,切换到另一个任务,如此循环.
类似于这样的执行顺序,就是协程.
swoole实现协程代码:
function task1(){
for ($i=0;$i<=300;$i++){
//写入文件,大概要3000微秒
usleep(3000);
echo "写入文件{$i} ";
Co::sleep(0.001);//挂起当前协程,0.001秒后恢复//相当于切换协程
}
}
function task2(){
for ($i=0;$i<=500;$i++){
//发送邮件给500名会员,大概3000微秒
usleep(3000);
echo "发送邮件{$i} ";
Co::sleep(0.001);//挂起当前协程,0.001秒后恢复//相当于切换协程
}
}
function task3(){
for ($i=0;$i<=100;$i++){
//模拟插入100条数据,大概3000微秒
usleep(3000);
echo "插入数据{$i} ";
Co::sleep(0.001);//挂起当前协程,0.001秒后恢复//相当于切换协程
}
}
$pid1 = go('task1');//go函数是swoole的开启协程函数,用于开启一个协程
$pid2 = go('task2');
$pid3 = go('task3');
以上代码,即可实现切换函数
协程与进程
由上面的协程执行顺序
中的代码2,我们很容易发现,协程其实只是运行在一个进程中的函数,只是这个函数会被切换到下一个执行,可以这么说:
协程的作用域
协程中的I/O连接
在协程中,要特别注意不能共用一个I/O连接,否则会造成数据异常. 用协程执行顺序
中的代码2解释,当task1,task2函数共用mysql连接,并都进行查询时,由于协程是交叉运行的,可能会造成task1获取到task1+task2查询出来的数据,也可能会丢失部分数据,被task2获取.