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
等引起协程切换的操作 - 不调用其它任何不可重入的函数
- 不要引用栈上内存