一个程序可以包含多个协程,可以对比与一个进程包含多个线程,因而下面我们来比较协程和线程。而多个线程相对独立,有自己的上下文,切换受系统控制;
而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程由当前协程来控制。
协程只是一串运行在进程中的任务代码,只是这些任务代码可以交叉运行。 注意,协程并不是多任务并行,属于多任务串行,每个进程在一个时间只执行了一个任务。
线程是可以多任务并行的 取决于cpu盒数
协程相比线程来说更轻量级 切换的开销更小
协程的作用域
由于协程就是进程中一串任务代码,所以它的全局变量,静态变量等变量都是共享的,包括了php的全局缓冲区.
所以,在开发之中,需要特别注意协程中的全局变量,静态变量,只要某一个协程内修改了,那将会影响全部的协程,在使用ob缓冲区函数拦截的时候,也得考虑是否会被其他协程的输出给污染.
用协程执行顺序中的代码2解释,当task1给$_GET['name']赋值为1时,task2读取$_GET['name']也会是1,task2将$_GET['name']赋值为2时,task3读取$_GET['name']也会是2
知识点补充:
局部变量:在函数内部中定义的变量,它的作用域为函数定义范围内。
静态变量: 静态变量属于静态存储方式,其存储空间为内存中的静态数据区(在静态存储区内分配存储单元),该区域中的数据在整个程序的运行期间一直占用这些存储空间(在程序整个运行期间都不释放),也可以认为是其内存地址不变,直到整个程序运行结束(相反,而 auto自动变量,即动态局部变量,属于动态存储类别,占动态存储空间,函数调用结束后即释放)。静态变量虽在程序的整个执行过程中始终存在,但是在它作 用域之外不能使用。
全局变量:在函数外部定义的变量,它的作用域从定义处一直到文件结尾,在函数中使用global调用 否则会由局部变量覆盖。全局变量虽属于静态存储方式,但不一定是静态变量,必须由 static加以定义后才能成为静态外部变量,或称静态全局变量
全局和静态变量要自己释放 $a=null unset($a);
使用global关键词声明的变量
使用static关键词声明的类静态变量、函数静态变量
PHP的超全局变量,包括$_GET、$_POST、$GLOBALS等
https://www.easyswoole.com/HttpServer/global.html $_GET、$_POST解决方法
执行过程中age全局变量会被污染
协程中的I/O连接
在协程中,要特别注意不能共用一个I/O连接,否则会造成数据异常. 用协程执行顺序中的代码2解释,当task1,task2函数共用mysql连接,并都进行查询时,由于协程是交叉运行的,可能会造成task1获取到task1+task2查询出来的数据,也可能会丢失部分数据,被task2获取.
由于协程的交叉运行机制,各个协程的I/O连接都必须是独立的,所以我们需要在每个协程都创建一个连接,但由于mysql,redis的连接数有限,以及连接的开启关闭需要消耗大量资源,所以我们可以使用连接池方案实现共用连接(只要保证每个连接每次只有一个协程在使用即可
注意事项 mysql redis连接池 可以使用invoker获取连接池 每次函数内部执行会自动释放 或者使用defer方式链接 协程结束会自动释放
WaitGroup
add 方法增加计数
done 表示任务已完成
wait 等待所有任务完成恢复当前协程的执行
WaitGroup 对象可以复用,add、done、wait 之后可以再次使用
如上图所示 wait方法可以挂起当成协程 等待所有任务结束后调用 这个就可以得到所有协程执行结果 再返回给前端
协程内部是没有返回值的 所有
$this->writeJson(Status::CODE_OK, $ret, 'success');不能在协程内部使用
Csp 并发模式 注意 可以得到协程执行结果
使用 子协程(go) + 通道(channel) 实现 Csp 并发模式并发执行。
当我们需要并发执行某些不相干的请求,并得到结果的时候
Context上下文管理器
在Swoole中,由于多个协程是并发执行的,因此不能使用类静态变量/全局变量保存协程上下文内容。使用局部变量是安全的,因为局部变量的值会自动保存在协程栈中,其他协程访问不到协程的局部变量。
因Swoole属于常驻内存,在特殊情况下声明变量,需要进行手动释放,释放不及时,会导致非常大的内存开销,使服务宕掉。