協程 C/C++ 擴展開發指南(1):內存安全

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變量和全局變量,堅持只用局部變量
  • 若必須訪問全局變量,必須保證只用於計算邏輯,不得存在任何IOSleep等引發協程切換的操做
  • 不調用其它任何不可重入的函數
  • 不要引用棧上內存
相關文章
相關標籤/搜索