php如何解決多線程讀寫同一檔案
大家都知道,php是沒有多線程概念的,盡管如此我們仍然可以用“不完美”的方法來模拟多線程。簡單的說,就是隊列處理。通過對檔案進行加鎖和解鎖,來實作。當一個檔案被一個使用者操作時,該檔案是被鎖定的,其他使用者隻能等待,确實不夠完美,但是也可以滿足一些要求不高的應用。
上限判斷,關鍵資料的寫入扣錢之類
用到了eaccelerator的記憶體鎖和檔案鎖,原理:判斷系統中是否安了eaccelerator如果有則使用記憶體鎖,如果不存在,則進行檔案鎖。根據帶入的key的不同可以實作多個鎖直接的并行處理 ,類似innodb的行級鎖。
具體類如下:file_put_contents($file, $string, lock_ex );
<?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);
}
?>
使用如下:
$lock = new cachelock($orderno); //要排隊通路檔案要相同,即name
$lock->lock();
//logic here
$lock->unlock();