天天看點

PHP并發 加悲觀鎖(1)

php如何解決多線程讀寫同一檔案

大家都知道,php是沒有多線程概念的,盡管如此我們仍然可以用“不完美”的方法來模拟多線程。簡單的說,就是隊列處理。通過對檔案進行加鎖和解鎖,來實作。當一個檔案被一個使用者操作時,該檔案是被鎖定的,其他使用者隻能等待,确實不夠完美,但是也可以滿足一些要求不高的應用。

上限判斷,關鍵資料的寫入扣錢之類

用到了eaccelerator的記憶體鎖和檔案鎖,原理:判斷系統中是否安了eaccelerator如果有則使用記憶體鎖,如果不存在,則進行檔案鎖。根據帶入的key的不同可以實作多個鎖直接的并行處理 ,類似innodb的行級鎖。

具體類如下:file_put_contents($file, $string, lock_ex );

PHP并發 加悲觀鎖(1)

<?php  

/** 

 * cachelock 程序鎖,主要用來進行cache失效時的單程序cache擷取,防止過多的sql請求穿透到資料庫 

 * 用于解決php在并發時候的鎖控制,通過檔案/eaccelerator進行程序間鎖定 

 * 如果沒有使用eaccelerator則進行進行檔案鎖處理,會做對應目錄下産生對應粒度的鎖 

 * 使用了eaccelerator則在記憶體中處理,性能相對較高 

 * 不同的鎖之間并行執行,類似mysql innodb的行級鎖 

 * 

 */  

class cachelock  

{  

    //檔案鎖存放路徑  

    private $path = null;  

    //檔案句柄  

    private $fp = null;  

    //鎖粒度,設定越大粒度越小  

    private $hashnum = 100;  

    //cache key  

    private $name;  

    //是否存在eaccelerator标志  

    private $eaccelerator = false;  

    /** 

     * 構造函數 

     * 傳入鎖的存放路徑,及cache key的名稱,這樣可以進行并發 

     * @param string $path 鎖的存放目錄 

     * @param string $name cache key 

     */  

    public function __construct($name, $path = 'lock')  

    {  

        //判斷是否存在eaccelerator,這裡啟用了eaccelerator之後可以進行記憶體鎖提高效率  

        $this->eaccelerator = function_exists("eaccelerator_lock");  

        if (!$this->eaccelerator) {  

            if (!is_dir($path)) { //目錄設定寫權限  

                mkdir($path, 0777);  

                chmod($path, 0777);  

            }  

            $this->path = realpath($path) . directory_separator . ($this->_mycrc32($name) % $this->hashnum) . '.txt';  

        }  

        $this->name = $name;  

    }  

     * crc32 

     * crc32封裝 相同的string會算出唯一值 

     * @param string $string 

     * @return int 

    private function _mycrc32($string)  

        $crc = abs(crc32($string));  

        if ($crc & 0x80000000) {  

            $crc ^= 0xffffffff;  

            $crc += 1;  

        return $crc;  

     * 加鎖 

     * enter description here ... 

    public function lock()  

        //如果無法開啟ea記憶體鎖,則開啟檔案鎖  

            //配置目錄權限可寫  

            $this->fp = fopen($this->path, 'w+');  

            if ($this->fp === false) {  

                return false;  

            return flock($this->fp, lock_ex);  

        } else {  

            return eaccelerator_lock($this->name);  

     * 解鎖 

    public function unlock()  

            if ($this->fp !== false) {  

                flock($this->fp, lock_un);  

                $this->deletecache();  

            //進行關閉  

            fclose($this->fp);  

            return eaccelerator_unlock($this->name);  

    //删除零時檔案  

    private function deletecache()  

        clearstatcache(); //清除檔案狀态緩存  

        //删除1小時前的檔案  

        $cachedir = dirname($this->path);  

        $files = scandir($cachedir);  

        foreach ($files as $file) {  

            if (strlen($file) > 2 && time() > (filemtime($cachedir . directory_separator . $file) + 3600)) {  

                @unlink($cachedir . directory_separator . $file);  

}  

?>  

 使用如下:

PHP并發 加悲觀鎖(1)

$lock = new cachelock($orderno); //要排隊通路檔案要相同,即name  

$lock->lock();  

//logic here  

$lock->unlock();