Swoole4 协程的出现使得 PHP 底层上从原来串行模式变成了并发模式. 有很多 PHP 的 C/C++ 扩展在开发时未能考虑到并发性, 可重入问题, 导致无法在 Swoole 协程中使用. 本文会详细讲解如何编写协程并发安全的 C/C++ 代码.
可重入性
示例代码:
- int t;
- void test1(int *x, int *y) {
- t = *x;
- *x = *y;
- //fun1 函数中可能会存在协程切换
- fun1();
- // 错误代码
- *y = t;
- }
t 是一个全局变量或者 static 静态变量
在协程 A 中调用了 test1 函数, 使用了全局变量 t
当函数内调用了 fun1(), 这个函数中如果发生了协程切换, 这时假如另外一个协程 B 也执行了 test1 函数, 那么 t 的值可能会被修改
协程 B 挂起时, 重新回到协程 A, 这时 * y = t, 会得到一个错误的值
引用栈内存
这也是一个严重的风险点. 协程 1 将自身栈内存的指针发送给另外一个协程 2, 协程 1 退出时会释放协程栈内存. 协程 2 的生命周期长于 1, 继续读写此内存, 就会导致 segment fault.
示例:
- void co1() {
- char buf[2048];
- // 这里启动一个新的协程, buf 是协程 1 栈上内存
- co2(buf);
- // 协程 1 退出时会释放栈内存
- }
- void co2(char *buf) {
- for(int i=0; i<2048; i++) {
- Coroutine::sleep(1);
- // 这里 buf 内存可能已经释放了
- buf[i] = 1;
- }
- }
协程安全代码
为了保证安全性, 在 Swoole4 协程编程中:
不要使用 static 变量和全局变量, 坚持只用局部变量
若必须访问全局变量, 必须保证只用于计算逻辑, 不得存在任何 IO 或 Sleep 等引起协程切换的操作
不调用其它任何不可重入的函数
不要引用栈上内存
来源: https://segmentfault.com/a/1190000018533664