PHP 网站中的线程锁使用指南
·
PHP 网站中的线程锁使用指南
在 PHP 网站开发中,线程锁(或更准确地说,进程锁)用于解决并发访问时的资源竞争问题。由于 PHP 通常是多进程运行的(如 PHP-FPM),而不是真正的多线程,所以我们主要讨论进程间锁的实现。
1. 文件锁 (flock)
php
<?php
// 获取锁文件路径
$lockFile = __DIR__ . '
// 打开锁文件(如果不存在会自动创建)
$lockHandle = fopen($lockFile, 'w+');
if (!$lockHandle) {
die('无法创建锁文件');
}
// 尝试获取独占锁
if (flock($lockHandle, LOCK_EX)) {
try {
// 这里放置需要加锁的代码
// 例如:修改共享资源、写入文件、更新数据库等
file_put_contents(__DIR__ . '/temp/counter.txt',
intval(file_get_contents(__DIR__ . '/temp/counter.txt')) + 1);
echo "操作成功完成\n";
} finally {
// 释放锁
flock($lockHandle, LOCK_UN);
}
} else {
echo "无法获取锁,另一个进程可能正在执行操作\n";
}
// 关闭文件句柄
fclose($lockHandle);
?>
2. Redis 分布式锁
对于分布式系统或多服务器环境,可以使用 Redis 实现分布式锁:
php
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$lockKey = 'resource_lock';
$lockTimeout = 10; // 锁超时时间(秒)
$requestId = uniqid(); // 唯一标识当前请求
// 尝试获取锁
$isLocked = $redis->set($lockKey, $requestId, ['nx', 'ex' => $lockTimeout]);
if ($isLocked) {
try {
// 获取锁成功,执行业务逻辑
echo "获取锁成功,执行业务逻辑\n";
// 模拟业务处理
sleep(5);
} finally {
// 释放锁(使用Lua脚本确保原子性)
$lua = <<<LUA
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
LUA;
$redis->eval($lua, [$lockKey, $requestId], 1);
}
} else {
echo "获取锁失败,资源正被占用\n";
}
?>
3. MySQL 锁(GET_LOCK/RELEASE_LOCK)
php
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$lockName = 'inventory_lock';
$timeout = 5; // 等待锁的超时时间(秒)
try {
// 尝试获取锁
$stmt = $pdo->query("SELECT GET_LOCK('$lockName', $timeout)");
$result = $stmt->fetchColumn();
if ($result == 1) {
try {
// 获取锁成功,执行业务逻辑
echo "获取数据库锁成功\n";
// 示例:更新库存
$pdo->exec("UPDATE products SET stock = stock - 1 WHERE id = 123");
} finally {
// 释放锁
$pdo->query("SELECT RELEASE_LOCK('$lockName')");
}
} else {
echo "获取数据库锁失败或超时\n";
}
} catch (PDOException $e) {
echo "数据库错误: " . $e->getMessage();
}
?>
4. Symfony Lock 组件(推荐用于框架项目)
如果你使用 Symfony 框架,可以使用其内置的 Lock 组件:
php
<?php
// 安装: composer require symfony/lock
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\FlockStore;
use Symfony\Component\Lock\Store\RedisStore;
// 文件锁方式
$store = new FlockStore();
// 或者Redis方式
// $redis = new \Predis\Client();
// $store = new RedisStore($redis);
$factory = new LockFactory($store);
$lock = $factory->createLock('inventory-update');
try {
if ($lock->acquire()) {
try {更多推荐
所有评论(0)