當前位置:
首頁 > 知識 > swoole 中的鎖及其應用

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 提供的函數: 即可

更多分享,敬請關注

本文來源網路,侵立刪!


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 PHP技術大全 的精彩文章:

PHP UDP攻擊查找源頭
php利用ob_start清除輸出和選擇性輸出的方法

TAG:PHP技術大全 |