基於 System V 共享記憶體 (shmop) 與信號量 (sem) 實作高效能、多進程安全的 PHP 記憶體佇列,支援任意資料類型 (透過序列化)。
<?php
class SHMQueue {
private $maxQSize = 0; // 最大佇列長度 (塊數)
private $front = 0; // 隊頭指標 (位元組偏移)
private $rear = 0; // 隊尾指標
private $blockSize = 256; // 每塊大小 (bytes)
private $memSize = 25600; // 共享記憶體總大小 (bytes)
private $shmId = 0; // shmop handle
private $filePtr = './shmq.ptr'; // 持久化指標檔
private $semId = 0; // 信號量 ID
public function __construct() {
$shmKey = ftok(__FILE__, 't');
$this->shmId = shmop_open($shmKey, "c", 0644, $this->memSize);
$this->maxQSize = $this->memSize / $this->blockSize;
$this->semId = sem_get($shmKey, 1);
sem_acquire($this->semId); // 進入臨界區
$this->init(); // 還原指標
}
private function init() {
if (file_exists($this->filePtr)) {
list($this->front, $this->rear) = explode('|', file_get_contents($this->filePtr)) + [0,0];
}
}
public function getLength() {
return (($this->rear - $this->front + $this->memSize) % $this->memSize) / $this->blockSize;
}
public function enQueue($value) {
if ($this->ptrInc($this->rear) == $this->front) return false; // 滿
$data = $this->encode($value);
shmop_write($this->shmId, $data, $this->rear);
$this->rear = $this->ptrInc($this->rear);
return true;
}
public function deQueue() {
if ($this->front == $this->rear) return false; // 空
$value = shmop_read($this->shmId, $this->front, $this->blockSize - 1);
$this->front = $this->ptrInc($this->front);
return $this->decode($value);
}
private function ptrInc($ptr) {
return ($ptr + $this->blockSize) % $this->memSize;
}
private function encode($value) {
$data = serialize($value) . "__eof";
if (strlen($data) > $this->blockSize - 1)
throw new Exception("Data size " . strlen($data) . " exceeds block size!");
return $data;
}
private function decode($value) {
return unserialize(explode("__eof", $value)[0]);
}
public function __destruct() {
file_put_contents($this->filePtr, "$this->front|$this->rear");
sem_release($this->semId); // 離開臨界區
}
}
// 生產者 $q = new SHMQueue(); $q->enQueue(['task_id' => 123, 'payload' => 'data']); unset($q); // 釋放鎖、持久化指標 // 消費者 $q = new SHMQueue(); $task = $q->deQueue(); unset($q);
ptrInc 取模實現循環,無需資料搬移。blockSize。sem_acquire/release 確保多進程並發安全 (PHP 預設無線程安全)。__destruct 寫入檔案,進程重啟後可恢復佇列狀態。__eof 分隔符配合 serialize/unserialize 支援任意類型。kernel.shmmax、kernel.shmall 系統參數,大佇列需調整 /etc/sysctl.conf。register_shutdown_function 強制 sem_release。