更新表时的竞争条件

时间:2013-12-06 01:38:48

标签: mysql sql asp.net-mvc-4 signalr

我有一个游戏,2个玩家通过使用mysql存储游戏状态的websockets相互玩游戏。玩家同时提交动作,服务器应做出相应的响应:

场景:Player1提交转弯。服务器检查数据库以查看最近的转弯是什么,并且:

  • 如果两位玩家都提交了该回合的动作,则会创建一个 使用Player1的动作输入进行新的转变。

  • 如果最近一次转弯没有Player1的动作,它会 更新数据库中的操作,然后执行游戏。

  • 如果最近一次转弯没有为player2输入,但它确实有一个输入为player1,它应该只更新数据库,然后等待player2做出回应。

当Player1提交他的回合时,问题出现了,玩家2提交他们的回合,然后两者几乎同时选择火力。这将导致2轮插入数据库,然后游戏不知道两个玩家都提交了他们的转弯(因为每个玩家认为他们已经创建了一个新的转弯)。

这是转牌表:

  • Id(int) - 主键
  • GameId(int) - 游戏桌的外键,游戏可以有 好几轮
  • 转数(int) - 表示此表所代表的转弯。
  • Player1Action(varchar) - 动作播放器1已经拍摄。
  • Player2Action(varchar) - 动作播放器2已经拍摄。

我不确定如何在不暴露这种竞争条件的情况下继续进行。除主键之外的任何列都不是唯一的,因此我无法检查这种方式,并且如果有某种方法可以阻止对现有GameId / TurnNumber组合的多次插入,则服务器需要知道以防止默认执行​​。

该项目使用ASP.NET MVC4和SignalR作为websocket协议,如果这有帮助的话。

编辑:

我知道在GameId和TurnNumber之间的表上创建一个复合唯一索引,但这不会向调用者指示存在插入冲突。

以下是一个示例(非常简化的)执行代码:

var turns = new List<Turn>();
var query = "select * from Turn where GameId = @id order by TurnNumber asc";
using (var conn = new MySqlConnection())
{
    using(var cmd = new MySqlCommand(conn, query))
    {
        cmd.Parameters.AddWithValue("@id", gameId);
        try
        {
            using(var reader = cmd.ExecuteReader())
            {
                while(reader.read())
                {
                     turns.Add(//Get Reader Data to make turn);
                }
            }
        }
        //Handle exceptions
    }
}
var lastTurn = turns[turns.Count - 1];

//This turn is old
if(TurnIsOld(lastTurn))
{
    //Create new turn and insert
    return;
}
//Player 1 has not taken an action
else if(Player1NoAction(turn))
{
    //Update the turn
    ExecuteGame(turns);
}
//Thus player 2 must have not taken an action
else
{
    //Update player 1 action
    return;
}

问题在于,当玩家1和玩家2在与其他玩家的if条件块尚未完成执行的大致相同的时间做出轮到他们的决定时,他们都可以执行第一个if条件。

0 个答案:

没有答案