PHP使用互斥鎖確保代碼的線程安全的操作示例
代碼的線程安全
在沒(méi)有互斥機(jī)制的情況下,多個(gè)進(jìn)程或線程可能會(huì)同時(shí)修改同一個(gè)資源,導(dǎo)致數(shù)據(jù)不一致的問(wèn)題。例如,在一個(gè)簡(jiǎn)單的庫(kù)存扣減操作中:
// 假設(shè)庫(kù)存為 10
$stock = 10;
// 多個(gè)請(qǐng)求同時(shí)到達(dá),每個(gè)請(qǐng)求都扣減庫(kù)存
for ($i = 0; $i < 20; $i++) {
$stock--;
}
// 最終庫(kù)存可能不是我們預(yù)期的 0,而是負(fù)數(shù)
這種情況在實(shí)際開(kāi)發(fā)中是不可接受的。那么,我們?nèi)绾未_保在 PHP 中實(shí)現(xiàn)代碼的互斥執(zhí)行呢?
wise-locksmith 庫(kù)介紹
wise-locksmith 是一個(gè) PHP 互斥鎖庫(kù),它提供了多種鎖機(jī)制來(lái)幫助我們解決線程安全問(wèn)題。并且這個(gè)庫(kù)不局限于任何框架,也就是說(shuō)只要是在 PHP 環(huán)境中,都可以使用。
下面,我們將詳細(xì)介紹這個(gè)庫(kù)的安裝、特性、基本與高級(jí)功能,并結(jié)合實(shí)際應(yīng)用場(chǎng)景展示其在項(xiàng)目中的使用。來(lái),繼續(xù)往下看吧~
安裝
首先,我們通過(guò) Composer 快速安裝 wise-locksmith:
composer require pudongping/wise-locksmith
特性
wise-locksmith 提供了多種鎖機(jī)制,以適應(yīng)不同的應(yīng)用場(chǎng)景:
- 文件鎖(flock):適用于單服務(wù)器環(huán)境。
- 分布式鎖(redisLock):適用于需要跨多個(gè)服務(wù)器或?qū)嵗姆植际江h(huán)境。
- 紅鎖(redLock):適用于 Redis 集群環(huán)境,提供更高的可靠性。
- 協(xié)程級(jí)別的互斥鎖(channelLock):適用于 Swoole 協(xié)程環(huán)境。
基本功能
文件鎖(flock)
文件鎖沒(méi)有任何依賴??赏ㄟ^(guò)可選的第 3 個(gè)參數(shù)參數(shù)設(shè)置鎖的超時(shí)時(shí)間,單位:秒。(支持浮點(diǎn)型,比如 1.5 表示 1500ms 也就是最多會(huì)等待 1500ms,如果沒(méi)有搶占到鎖,那么則主動(dòng)放棄搶鎖,同時(shí)會(huì)拋出 Pudongping\WiseLocksmith\Exception\TimeoutException 異常) 設(shè)置成 Pudongping\WiseLocksmith\Lock\File\Flock::INFINITE_TIMEOUT 時(shí),表示永不過(guò)期,則當(dāng)前一直會(huì)阻塞式搶占鎖,直到搶占到鎖為止。默認(rèn)值為:Pudongping\WiseLocksmith\Lock\File\Flock::INFINITE_TIMEOUT。
文件鎖是最簡(jiǎn)單的一種鎖,適用于單服務(wù)器環(huán)境。它通過(guò)鎖定一個(gè)文件來(lái)實(shí)現(xiàn)互斥。以下是一個(gè)簡(jiǎn)單的文件鎖示例:
<?php
require 'vendor/autoload.php';
use Pudongping\WiseLocksmith\Locker;
$path = tempnam(sys_get_temp_dir(), 'wise-locksmith-flock-');
$fileHandler = fopen($path, 'r+');
$locker = new Locker();
try {
$locker->flock($fileHandler, function () use ($stock) {
// 這里寫(xiě)你想保護(hù)的代碼
$stock--;
// 確保操作的原子性
});
} catch (\Exception $e) {
// 處理異常
}
fclose($fileHandler);
unlink($path);
分布式鎖(redisLock)
需要依賴 redis 擴(kuò)展。可通過(guò)可選的第 3 個(gè)參數(shù)設(shè)置鎖的超時(shí)時(shí)間,單位:秒。(支持浮點(diǎn)型,比如 1.5 表示 1500ms 也就是最多會(huì)等待 1500ms,如果沒(méi)有搶占到鎖,那么則主動(dòng)放棄搶鎖,同時(shí)會(huì)拋出 Pudongping\WiseLocksmith\Exception\TimeoutException 異常) 默認(rèn)值為:5。第 4 個(gè)參數(shù)為當(dāng)前鎖的具有唯一性的值,除非有特殊情況下需要設(shè)置,一般不需要設(shè)置。
在分布式系統(tǒng)中,我們經(jīng)常需要確??缍鄠€(gè)服務(wù)器的操作是互斥的。redisLock 提供了這樣的功能:
<?php
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$locker = new Locker();
try {
$locker->redisLock($redis, 'redisLock', function () use ($stock) {
// 這里寫(xiě)你想保護(hù)的代碼
$stock--;
// 確保操作的原子性
});
} catch (\Exception $e) {
// 處理異常
}
高級(jí)功能
紅鎖(redLock)
redLock 鎖所需要設(shè)置的參數(shù)和 redisLock 鎖除了第一個(gè)參數(shù)有區(qū)別以外,其他幾個(gè)參數(shù)完全一致。redLock 鎖是 redisLock 鎖的集群實(shí)現(xiàn)。
紅鎖是分布式鎖的一種高級(jí)實(shí)現(xiàn),它在 Redis 集群環(huán)境中提供更高的可靠性:
<?php
$redisInstances = [
['host' => '127.0.0.1', 'port' => 6379],
// 其他 Redis 實(shí)例...
];
$redis = array_map(function ($v) {
$redis = new \Redis();
$redis->connect($v['host'], $v['port']);
return $redis;
}, $redisInstances);
$locker = new Locker();
try {
$locker->redLock($redis, 'redLock', function () use ($stock) {
// 這里寫(xiě)你想保護(hù)的代碼
$stock--;
// 確保操作的原子性
});
} catch (\Exception $e) {
// 處理異常
}
協(xié)程級(jí)別的互斥鎖(channelLock)
使用此鎖時(shí),需要安裝 swoole 擴(kuò)展。且版本必須大于等于 4.5??赏ㄟ^(guò)可選的第 3 個(gè)參數(shù)設(shè)置鎖的超時(shí)時(shí)間,單位:秒。(支持浮點(diǎn)型,比如 1.5 表示 1500ms 也就是最多會(huì)等待 1500ms,如果沒(méi)有搶占到鎖,那么則主動(dòng)放棄搶鎖,同時(shí)直接返回 false 表示沒(méi)有搶占到鎖) 設(shè)置成 -1 時(shí),表示永不過(guò)期,則當(dāng)前一直會(huì)阻塞式搶占鎖,直到搶占到鎖為止。默認(rèn)值為:-1。
在 Swoole 協(xié)程環(huán)境中,channelLock 提供了協(xié)程級(jí)別的互斥鎖:
<?php
$locker = new Locker();
try {
$locker->channelLock('channelLock', function () use ($stock) {
// 這里寫(xiě)你想保護(hù)的代碼
$stock--;
// 確保操作的原子性
});
} catch (\Exception $e) {
// 處理異常
}
實(shí)際應(yīng)用場(chǎng)景
假設(shè)我們有一個(gè)高并發(fā)的電商平臺(tái),需要在用戶下單時(shí)扣減庫(kù)存。使用 wise-locksmith 庫(kù),我們可以確保在任何時(shí)候只有一個(gè)請(qǐng)求能夠修改庫(kù)存,從而避免超賣(mài)的問(wèn)題。以下是如何在實(shí)際項(xiàng)目中使用 wise-locksmith 來(lái)實(shí)現(xiàn)庫(kù)存扣減的互斥操作:
<?php
// 假設(shè)我們有一個(gè)全局的 Redis 連接實(shí)例
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
// 庫(kù)存扣減操作
function decreaseStock($productId, $quantity) {
$locker = new Locker();
try {
$locker->redisLock($redis, "stock_lock_{$productId}", function () use ($productId, $quantity) {
// 這里寫(xiě)你想保護(hù)的代碼
// 假設(shè)我們從數(shù)據(jù)庫(kù)中獲取當(dāng)前庫(kù)存
$stock = getStockFromDatabase($productId);
if ($stock >= $quantity) {
// 更新庫(kù)存
updateStockInDatabase($productId, $stock - $quantity);
}
});
} catch (\Exception $e) {
// 處理異常
}
}
// 調(diào)用扣減庫(kù)存函數(shù)
decreaseStock(123, 1);
結(jié)語(yǔ)
通過(guò) wise-locksmith 庫(kù),我們可以輕松地在 PHP 應(yīng)用中實(shí)現(xiàn)代碼的互斥執(zhí)行,無(wú)論是單服務(wù)器環(huán)境還是分布式系統(tǒng)。
希望這篇文章能幫助你更好地理解和使用 wise-locksmith 庫(kù),確保你的代碼在多線程環(huán)境下的線程安全。如果你覺(jué)得這個(gè)庫(kù)對(duì)你有點(diǎn)兒幫助,那就請(qǐng)幫忙點(diǎn)個(gè) Star 呀~
以上就是PHP使用互斥鎖確保代碼的線程安全的詳細(xì)內(nèi)容,更多關(guān)于PHP互斥鎖線程安全的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
php的優(yōu)點(diǎn)總結(jié) php有哪些優(yōu)點(diǎn)
在本篇文章里小編給各位整理了關(guān)于php的優(yōu)點(diǎn)的總結(jié)內(nèi)容以及相關(guān)知識(shí)點(diǎn)整理,需要的朋友們學(xué)習(xí)下。2019-07-07
PHP laravel實(shí)現(xiàn)配置使用多數(shù)據(jù)庫(kù)
有的時(shí)候,我們?cè)陂_(kāi)發(fā)的過(guò)程中需要連接多個(gè)數(shù)據(jù)庫(kù)。Laravel框架中早已為我們想到了這樣的需求。這篇文章主要為大家介紹了laravel配置使用多數(shù)據(jù)庫(kù)的方法,需要的可以參考一下2022-10-10
使用php語(yǔ)句將數(shù)據(jù)庫(kù)*.sql文件導(dǎo)入數(shù)據(jù)庫(kù)
這篇文章主要介紹了如何使用php語(yǔ)句將數(shù)據(jù)庫(kù)*.sql文件導(dǎo)入數(shù)據(jù)庫(kù),需要的朋友可以參考下2014-05-05
PHP實(shí)現(xiàn)登陸并抓取微信列表中最新一組微信消息的方法
這篇文章主要介紹了PHP實(shí)現(xiàn)登陸并抓取微信列表中最新一組微信消息的方法,涉及php針對(duì)微信接口的登陸、抓取、轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2017-07-07

