swoole 中的鎖及其應用
時間不早了, 盡量言簡意賅一些.
怎麼快速學習鎖:
鎖是什麼 -> 鎖的概念
為什麼要用鎖 -> 鎖的使用場景
怎麼使用鎖 ->show me the code
需要了解的相關概念:
多進程 / 多線程
臨界區 / 競態資源
要培養鎖的意識:
並發場景下的數據安全, 比如 session 覆蓋( PHP的Session數據同步問題 )
與鎖相關問題的幾個維度
鎖的種類 : 鎖的規則 : 鎖的場景 = 1 : 1 : 1
swoole 中支持的鎖(lock), 相關代碼在 下, 代碼很清晰, 適合閱讀:
互斥鎖 SWOOLE_MUTEX
自旋鎖 SWOOLE_SPINLOCK
讀寫鎖 SWOOLE_RWLOCK
文件鎖 SWOOLE_FILELOCK
信號量 SWOOLE_SEM
鎖的生命周期: 創建鎖 -> 加鎖 -> 解鎖
$lock=newSwooleLock(SWOOLE_MUTEX); // 參數是鎖的種類, 下面細說$lock->lock();$lock->unlock();
注意: 這裡並沒有看到鎖的銷毀,受限於 PHP 的多進程模型,只能這樣, 所以官網特地提到
請勿在onReceive等回調函數中創建鎖,否則底層的GlobalMemory內存會持續增長,造成內存泄漏
是否阻塞: 申請加鎖是否會阻塞
$lock->lock();//阻塞, 直到成功申請到鎖
$lock->trylock();//不會阻塞, 立即返回搶鎖的結果
搶鎖 = 申請鎖, 不要被中文誤導, 這裡是同一個意思
因為鎖的規則不同,信號量 SWOOLE_SEM不支持 這樣非阻塞調用方式
鎖的實現方式
$lock=newSwooleLock(SWOOLE_FILELOCK,string$lockfile); // 文件鎖$lock=newSwooleLock(SWOOLE_MUTEX); // 其他鎖
文件鎖 SWOOLE_FILELOCK使用文件來作為鎖,其他鎖使用內存, 所以其他類型的鎖必須在父進程內創建.
互斥鎖 SWOOLE_MUTEX
這個概念理解起來最容易:互斥鎖 = 獨佔鎖 = 排它鎖 = 寫鎖.
鎖的規則: 顧名思義, 就是這個鎖當前只能有一個 進程/線程 使用, 其他 進程/線程 只能等待
實現: swoole 底層使用 系列函數實現.
場景: 任何需要排他/獨佔的場景都適用
自旋鎖 SWOOLE_SPINLOCK
自旋鎖和互斥鎖類似, 只是在阻塞時的狀態不一樣:
互斥鎖沒有申請到鎖時, 通常會進入睡眠狀態, 這樣當鎖釋放時, 就需要喚醒; 自旋鎖則會一直循環在那裡等待鎖
實現: swoole 底層使用 系列函數實現.
場景: 鎖保持時間非常短的情況, 減少睡眠 -> 喚醒的成本
讀寫鎖 SWOOLE_RWLOCK
$lock->lock();//寫鎖
$lock->lock_read();//讀鎖
寫鎖上面已經見過了, 讀寫鎖由讀鎖 + 寫鎖組成
鎖的規則: 如果當前有讀鎖, 可以繼續申請讀鎖, 但是不能申請寫鎖;寫鎖的規則和上面一致
實現: swoole 底層使用 系列函數實現.
場景: 讀多寫少的場景, 減少鎖的阻塞帶來的性能損耗
文件鎖 SWOOLE_FILELOCK
除了使用文件作為鎖, 規則和場景和讀寫鎖 SWOOLE_RWLOCK一致
實現: swoole 底層使用 函數實現.
信號量 SWOOLE_SEM
信號量的規則稍微會複雜一些, 會涉及到 2 個操作:
鎖的規則: 如果信號量 >= 0, 則可以執行 P 操作, 並將信號量減一; 如果信號量
實現: swoole 底層調用 實現.
場景: 信號量操作可以想像成倉庫進貨出貨的過程, 倉庫是空的, 就不能用 P 操作來出貨了, 倉庫是滿的, 就不能用 V 操作來繼續進貨了
注意: 信號量沒有 方法; swoole 沒有提供容量的設置, 退化為容量為 1 的情況了, 其實和互斥鎖 SWOOLE_MUTEX差不多了
寫在最後
到目前為止, swoole 當中使用到的鎖都聊到了, 最後說一個彩蛋. 在 2017北京PHP開發者年會上, thinkpc 講到exec的梗:
使用 導致服務一直阻塞在那裡, 後來和韓老大說這個事, 韓老大就給加上了一個 timeout.
$lock->lockwait($timeout);
所以我猜測 這個只有Mutex類型才支持的方法, 是不是也是這種情況.
2017北京PHP開發者年會 - 盜圖自 葉金榮 大大
寫在最後
swoole wiki - 還是優先推薦的第一手資料.
理解一個不太熟悉的概念時, 可以嘗試劃一划不同維度, 在這些維度上面找相同 / 不同.
Yii 框架中特定提供了mutex模塊, 並提供了不同種類的實現的方式, 可以移步看我之前的blog - yii源碼解讀
使用 file 實現的 mutex:
// 加鎖
protectedfunctionacquireLock($name, $timeout =){ $file = fopen($this->getLockFilePath($name),"w+");
if($file ===false) {
returnfalse; }if($this->fileMode !==null) { @chmod($this->getLockFilePath($name),$this->fileMode); } $waitTime =;
while(!flock($file, LOCK_EX | LOCK_NB)) {// 加鎖$waitTime++;
if($waitTime > $timeout) {// 超時邏輯fclose($file);
returnfalse; } sleep(1); }$this->_files[$name] = $file;returntrue;}// 解鎖
protectedfunctionreleaseLock($name){
if(!isset($this->_files[$name]) ||
!flock($this->_files[$name], LOCK_UN)) {// 解鎖returnfalse; }else{ fclose($this->_files[$name]); unlink($this->getLockFilePath($name));
unset($this->_files[$name]);
returntrue; }}
核心其實是 函數:
LOCK_EX, 英文對應 exclusive lock, 排它鎖
LOCK_UN, 英文對應 unlock, 解鎖
LOCK_SH, 英文對應 share lock, 共享鎖
LOCK_NB, 英文對應 non block, 非阻塞
使用 mysql 實現的 mutex:
// 加鎖protectedfunctionacquireLock($name, $timeout =){
return(bool)$this->db ->createCommand("SELECT GET_LOCK(:name, :timeout)",
[":name"=> $name,":timeout"=> $timeout]) ->queryScalar();}// 解鎖
protectedfunctionreleaseLock($name){
return(bool)$this->db ->createCommand("SELECT RELEASE_LOCK(:name)",
[":name"=> $name]) ->queryScalar();}
調用 mysql 提供的函數: 即可
更多分享,敬請關注
本文來源網路,侵立刪!
![](https://pic.pimg.tw/zzuyanan/1488615166-1259157397.png)
![](https://pic.pimg.tw/zzuyanan/1482887990-2595557020.jpg)
※PHP UDP攻擊查找源頭
※php利用ob_start清除輸出和選擇性輸出的方法
TAG:PHP技術大全 |