PHP 常被視為「同步阻塞」語言,但透過 System V IPC 擴展(sysvsem、sysvshm、sysvmsg),可實作真正的事件驅動、多進程協作架構。本文從基礎 API 到完整事件循環示範。
| 機制 | 函數族 | 適用場景 |
|---|---|---|
| 訊號量 | sem_* | 進程同步、互斥鎖、資源計數 |
| 共享記憶體 | shm_* / shmop_* | 高速進程間資料交換、快取 |
| 訊息佇列 | msg_* | 非同步命令傳遞、事件通知、工作分發 |
// ftok:將路徑+專案字元轉為 IPC Key(所有 IPC 需共用同 Key)
$key = ftok(__FILE__, 'a'); // 專案字元需為單一字元
// declare(ticks=N):每 N 條低級指令觸發 tick 事件
// 搭配 register_tick_function 可實現「協作式多工」輪詢
declare(ticks=10);
register_tick_function('checkMessages');
echo " -> 處理用戶登入: $data\n",
2 => echo " -> 處理訂單建立: $data\n",
default => echo " -> 未知事件類型: $type\n",
};
}
// 註冊 tick 輪詢(每 50 指令檢查一次)
register_tick_function('pollQueue', $queue);
declare(ticks=50) {
echo "事件循環啟動,模擬發送事件...\n";
// 模擬外部發送事件(實際應由其他進程送入)
for ($i = 1; $i <= 20; $i++) {
if ($i % 5 === 0) {
msg_send($queue, 1, "UserLogin:$i");
}
if ($i % 7 === 0) {
msg_send($queue, 2, "OrderCreate:$i");
}
// 執行一些運算觸發 tick
$x = sqrt($i) * 123;
}
}
msg_remove_queue($queue);
echo "事件循環結束\n";
事件循環啟動,模擬發送事件... [Event] 收到類型 1: UserLogin:5 -> 處理用戶登入: UserLogin:5 [Event] 收到類型 1: UserLogin:10 -> 處理用戶登入: UserLogin:10 [Event] 收到類型 2: OrderCreate:14 -> 處理訂單建立: OrderCreate:14 [Event] 收到類型 1: UserLogin:15 -> 處理用戶登入: UserLogin:15 ...
0, 'hits' => []];
// 更新
$data['count']++;
$data['hits'][] = ['time' => microtime(true), 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'cli'];
// 只保留最近 100 筆
$data['hits'] = array_slice($data['hits'], -100);
// 寫回(序列化後填充固定長度)
$serialized = serialize($data);
$padded = str_pad($serialized, 1024, "\0");
shmop_write($shmId, $padded, 0);
echo "總存取次數: {$data['count']}\n";
echo "最近 5 次: \n";
print_r(array_slice($data['hits'], -5));
class SemaphoreLock {
private $semId;
private $key;
public function __construct(string $lockName) {
$this->key = ftok(__FILE__, $lockName[0]);
$this->semId = sem_get($this->key, 1, 0666, 1); // 1 個資源
}
public function acquire(bool $blocking = true): bool {
return sem_acquire($this->semId, $blocking);
}
public function release(): bool {
return sem_release($this->semId);
}
public function __destruct() {
// 注意:sem_remove 會刪除系統層級訊號量,謹慎使用
// 通常由專門清理腳本處理
}
}
// 使用:確保關鍵區段單一進程執行
$lock = new SemaphoreLock('cron_job');
if ($lock->acquire(false)) { // 非阻塞嘗試
echo "獲得鎖,執行排程任務...\n";
sleep(5); // 模擬長時間任務
$lock->release();
echo "釋放鎖\n";
} else {
echo "另一進程正在執行,跳過本次\n";
}
ftok 依賴 inode,容器環境掛載同檔案可能產生相同 Key,建議加入專案前綴msg_remove_queue、sem_remove、shmop_delete),否則重啟前永駐declare(ticks=N) 會大幅降低執行速度,生產環境建議改用 pcntl_signal + SIGALRM 或 ReactPHP/Swoole 事件循環igbinary 或 msgpack,比 serialize 快 3-5 倍且體積更小GitHub Gist:php-ipc-demo(含生產者/消費者分離腳本、優雅關機處理)