只是為了澄清上面「程序」的含義
在 Apache 網頁伺服器上,許多 PHP 請求會在同一個程序空間內執行,因為它是多執行緒的。然而,任何被腳本取得和獲取但未釋放和移除的信號量,每次腳本終止時仍會被 PHP 直譯器自動清除。
寄送電子郵件前請移除任何垃圾郵件!
(PHP 4, PHP 5, PHP 7, PHP 8)
sem_acquire — 取得號誌
預設情況下,sem_acquire() 會阻斷(如有必要),直到可以取得號誌為止。如果取得號誌將導致其達到的號誌最大數量被超過,則嘗試取得其已取得之號誌的行程將永遠阻斷。
處理請求後,行程取得但未明確釋放的任何號誌將會自動釋放,並產生警告。
只是為了澄清上面「程序」的含義
在 Apache 網頁伺服器上,許多 PHP 請求會在同一個程序空間內執行,因為它是多執行緒的。然而,任何被腳本取得和獲取但未釋放和移除的信號量,每次腳本終止時仍會被 PHP 直譯器自動清除。
寄送電子郵件前請移除任何垃圾郵件!
sem_acquire() 是阻塞的,這意味著使用相同信號量的後續呼叫將無限期阻塞,直到信號量被釋放。這確保了序列化,但如果您只想檢查是否應該繼續,這並不是很實用。遺憾的是,PHP 尚未支援以非阻塞方式查詢信號量狀態的任何方法。
使用共享記憶體(shm_ 函數)似乎可以手動組裝這樣一個機制。但是,請注意,這並不容易,而且最終沒有效率。例如,您不能簡單地選擇一個共享記憶體變數,儲存信號量鍵並查詢它。這樣的操作將是非事務性和非原子的,例如,兩個或多個平行程序有可能在其中一個程序設法將其標記為「已鎖定」之前,設法從共享記憶體變數中讀取「未鎖定」。您必須使用(阻塞)信號量來序列化對共享記憶體變數的存取,從而重新產生您正試圖解決的問題。
換句話說,如果非阻塞查詢對您至關重要,您需要要求 PHP 設計者解決此問題,或者選擇另一種機制來進行鎖定,一種已經具有此功能的機制。
請注意,當您重設 $sem_identifier 時,信號量將不再阻塞!
這段程式碼無效
$key = ftok(__FILE__,'m');
$a = sem_get($key);
sem_acquire($a);
$a = false;
而這段程式碼有效
$key = ftok(__FILE__,'m');
$a = sem_get($key);
sem_acquire($a);
//$a = false;
所以:使用獨特的變數名稱作為你的識別符號!
我最近不得不使用信號量來避免同時執行資料庫更新。
為了測試它,我在同一個桌面上使用了兩個瀏覽器視窗(Chrome)。
在這種情況下,信號量沒有按預期工作。
同樣,對檔案使用 flock 鎖定也沒有按預期工作。
從命令列執行指令碼確實按預期工作。
一旦我使用兩個不同的瀏覽器,一個在我的桌面上,另一個在虛擬機器中,它就會像預期一樣工作。
希望這可以幫助任何人避免關於這個主題的奇怪除錯過程...
如果您需要非阻塞信號量,以下是如何實現它的一個範例。使用一個共享記憶體變數來標記鎖是否存在,然後在針對該變數的操作周圍使用信號量。我將我的共享變數稱為「token」。
<?php
echo '<pre>';
$resourceSemaphore = sem_get(7);
$tokenSemaphore = sem_get(8);
$tokenValue = shm_attach(9, 100);
function myEcho($v) {
echo microtime() . ' ' . $v . "\n";
}
// sem_remove($resourceSemaphore);
// sem_remove($tokenSemaphore);
// exit();
function try_lock() {
global $resourceSemaphore, $tokenSemaphore, $tokenValue;
myEcho('begin try_lock()');
myEcho('acquire token semaphore');
sem_acquire($tokenSemaphore);
myEcho(' token semaphore acquired');
$tmp = shm_get_var($tokenValue, 6);
myEcho(' token value: ' . var_export($tmp, true));
$exit = $tmp;
if (!$exit) {
$tmp = shm_put_var($tokenValue, 6, true);
$tmp = shm_get_var($tokenValue, 6);
myEcho(' token new value: ' . var_export($tmp, true));
}
myEcho('release token semaphore');
sem_release($tokenSemaphore);
if ($exit) return false;
myEcho('acquire resource semaphore');
sem_acquire($resourceSemaphore);
myEcho(' resource semaphore acquired');
return true;
}
function release() {
global $resourceSemaphore, $tokenSemaphore, $tokenValue;
myEcho('release resource semaphore');
sem_release($resourceSemaphore);
myEcho('acquire token semaphore');
sem_acquire($tokenSemaphore);
myEcho(' token semaphore acquired');
$tmp = shm_get_var($tokenValue, 6);
myEcho(' token value: ' . var_export($tmp, true));
$tmp = shm_put_var($tokenValue, 6, false);
$tmp = shm_get_var($tokenValue, 6);
myEcho(' token new value: ' . var_export($tmp, true));
myEcho('release token semaphore');
sem_release($tokenSemaphore);
}
for ($triesLeft = 5; $triesLeft > 0 && !try_lock(); $triesLeft--) {
myEcho('failed to acquire resource');
myEcho('wait for 1 sec');
sleep(1);
myEcho('try again');
}
myEcho(' access the resource for 4 sec');
//paste here your code, accessing your resource
sleep(4);
release();
myEcho('the end');
?>
When I execute this script in two parallel instances, I get the following output
-------(第一個實例)----------------------------------------
... 482 開始 try_lock()
... 482 取得 token 信號量
... 482 token 信號量已取得
... 482 token 值:false
... 482 token 新值:true
... 482 釋放 token 信號量
... 482 取得資源信號量
... 482 資源信號量已取得
... 482 訪問資源 4 秒
... 486 釋放資源信號量
... 486 取得 token 信號量
... 486 token 信號量已取得
... 486 token 值:true
... 486 token 新值:false
... 486 釋放 token 信號量
... 486 結束
-------(第二個實例)----------------------------------------
... 485 開始 try_lock()
... 485 取得 token 信號量
... 485 token 信號量已取得
... 485 token 值:true
... 485 釋放 token 信號量
... 485 無法取得資源
... 485 等待 1 秒
...
... 486 等待 1 秒
... 487 再次嘗試
... 487 開始 try_lock()
... 487 取得 token 信號量
... 487 token 信號量已取得
... 487 token 值:false
... 487 token 新值:true
... 487 釋放 token 信號量
... 487 取得資源信號量
... 487 資源信號量已取得
... 487 訪問資源 4 秒
... 491 釋放資源信號量
... 491 取得 token 信號量
... 491 token 信號量已取得
... 491 token 值:true
... 491 token 新值:false
... 491 釋放 token 信號量
... 491 結束
很遺憾,PHP 目前不支援非阻塞信號量。
如果需要這樣做,您可以利用信號量 (Semaphore) 和共享內存 (Shared Memory) 來創建您自己的非阻塞鎖定機制。
使用共享內存變量來標記鎖是否存在,然後在針對該變量的操作周圍使用信號量。