基本上我有一个用PHP(Laravel)构建的在线游戏。它的功能非常简单。当玩家装备物品时,它会检查是否已配备相同类型的物品。如果没有配备相同类型的物品,它将继续装备物品。
这可以通过简单地将Inventory模型上的装备标志更改为1来完成。像这样
if($alreadyequippeditem == false){
//equip current item
$inventory = \App\Inventory::find($item->pivot->id);
$inventory->equipped = 1;
$inventory->save();
}
然而,如果一个人足够快,一个人可以同时装备两个(或甚至更多)同一时间的物品。基本上绕过支票。
在我看来,这两个请求正在由服务器在同一时间处理,并且它们在完全相同的时间装备这些项目并且它基本上绕过了检查。这可以解释为什么可以配备两个项目,因为在请求运行时,他们不会相互认识。我可能错了,但这是我能想到的唯一解释。
我该如何解决这个问题?有没有办法限制它,所以只有1个请求同时发送?当我构建具有核心PHP且没有框架的系统时,我无法回想起曾经遇到过这个问题,因此我不知道它是否由laravel配置引起。我知道我可以使用队列绕过它(尽管我还没有深入研究过它),但我更愿意找到一个在整个站点范围内工作的解决方案,因为这个特定的问题影响了系统的许多方面。
答案 0 :(得分:3)
您所体验的是race condition的经典教科书示例:
竞争条件是设备发生的不良情况 或系统尝试同时执行两个或多个操作, 但由于设备或系统的性质,操作必须 按正确的顺序完成,以便正确完成。
幸运的是,这不是一个新问题,也不是一个很难解决的问题。特别是使用Laravel时:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Inventory;
use App\Player;
use Request;
class BustRaceConditionsController extends Controller
{
public function EquipItem(Request $request)
{
\DB::beginTransaction();
$player = $player->where("id", $request->user()->player_id)->lockForUpdate()->first();
$success = true;
try {
/* some logic here to check if item is equiped or not */
if (itemIsEquiped()) {
$success = false;
} else {
/* lets equip the item, then save it, and then we need to commit it to the DB */
$player->itemEquip = $item;
$player->save();
\DB::commit();
}
} catch (\Exception $e) {
$success = false;
\DB::rollback();
}
return ['success' => $success];
}
}
lockForUpdate()
。这是为了确保在我的更改提交或失败之前,其他任何内容都无法读取或写入,从而释放锁定。我希望这有帮助!