在PHP中,加锁是一种非常重要的机制。当多个进程或线程对同一个资源进行读写时,加锁可以保证数据的一致性和正确性。PHP中的加锁可以分为两种:文件锁和共享内存锁。
文件锁是使用文件系统提供的锁机制,来控制多进程或多线程对该文件的访问。PHP中提供了 flock 函数来实现文件锁的功能。下面的代码展示了如何使用 flock 函数来进行文件锁的控制。
/**
* @Notes: 用户领取优惠券
* @Author:DurkBlue
* @Time: 2024/1/31 10:30
* @param int $userId
* @param int $voucherId
* @return bool
* @Interface doReceiverCoupon
*/
public static function doReceiverCoupon(int $userId, int $voucherId): bool{
try {
// 文件加锁,防止多线程并发下导致的优惠券可领取等数据错乱异常
$myFile = fopen('/flock.txt', 'r+');
if (flock($myFile, LOCK_EX)) {
$couponObj = DurkCoupon::findOrEmpty($voucherId);
if($couponObj->isEmpty()) throw new Exception("未找到该优惠券");
// 有无设置过期时间
$nowTime = time();
$validate_time = 0;
if($couponObj->get_over_hour > 0){
$addValidateSecond = $couponObj->get_over_hour * 3600;
$validate_time = $nowTime + $addValidateSecond;
}
$insert_data = [
'voucher_id' => $voucherId,
'voucher_title' => $couponObj->voucher_title,
'user_id' => $userId,
'credit' => $couponObj->credit,
'limit_money' => $couponObj->limit_money,
'type' => $couponObj->type,
'limit_officer_list' => $couponObj->limit_officer_list,
'validate_time' => $validate_time,
'create_time' => $nowTime
];
$userCouponObj = DurkUserCoupon::create($insert_data);
if(!empty($userCouponObj->id)){
// 给优惠券加上领取张数,并判断是否达到完结状态
}
// 在此处进行文件的读写操作
flock($myFile, LOCK_UN);
}
} catch (\Exception $e) {
self::$error = $e->getMessage();
return false;
}
}
PHP自带了文件锁函数:
bool flock ( int $handle , int $operation [, int &$wouldblock ] )
$handle 是打开的文件指针;
$operation 可以是
“LOCK_SH”,共享锁定;“LOCK_EX”,独占锁定;“LOCK_UN”,释放锁定;“LOCK_NB”,防止flock锁定时堵塞。
这里主要说说“LOCK_EX”和“LOCK_NB”。
比如我们有两个文件,如下。
flocka.php
flockb.php
先运行flocka.php,然后马上运行flockb.php。
结果:
11111111
22222222
22222222
22222222
22222222
22222222
11111111
11111111
11111111
11111111
说明不加文件锁时,两个文件会同时对txt文件进行写入操作。
下面修改一下两个php文件的代码。
flocka.php
flockb.php
同样先运行flocka.php,然后马上运行flockb.php。
会发现在flocka.php运行结束前,flockb.php一直处于等待状态,只有当flocka.php运行结束后,flockb.php才会继续执行。
输出结果:
11111111
11111111
11111111
11111111
11111111
22222222
22222222
22222222
22222222
22222222
另外,在执行flock时,文件锁会自动释放。
php还提供了一个共享内存锁来对程序进行加锁功能,共享内存锁是通过操作系统提供的共享内存实现的。PHP中提供了 shmop 函数和 sem 函数来实现共享内存锁的功能。下面的代码展示了如何使用 sem 函数进行共享内存锁的控制。
$key = 1234; $sem_id = sem_get($key); if (sem_acquire($sem_id)) { // 在此处进行对共享内存的读写操作 sem_release($sem_id); } else { // 锁定共享内存失败 } sem_remove($sem_id);
上述代码首先创建了一个信号量,并使用 sem_acquire 函数进行加锁。在操作共享内存后,又使用 sem_release 函数释放了锁。这样可以确保在同一时刻只有一个进程或线程访问共享内存。