gpt4 book ai didi

c# - 通过并行调用 WCF 避免相同的代码运行两次

转载 作者:行者123 更新时间:2023-11-30 21:57:34 29 4
gpt4 key购买 nike

我正在制作一个小游戏,用户可以在其中使用 WCF 发送 Action ,当游戏中的所有 Action 都已发送后,游戏会模拟下一轮。

数据存储在 MSSQL 数据库中并通过 EntityFramework 访问。

在每次调用结束时检查是否已发送所有移动以及是否应开始模拟。

这里有一个问题。如果两个玩家都在一个非常小的时间窗口内发送他们的移动,那么两个线程都有可能检查是否所有移动都已发送,发现他们已经发送并开始两个模拟,直到其中一个足够远以将状态设置为模拟(并阻止任何进一步的尝试)。

为了解决这个问题,我写了以下检查。

总体思路是这样的:

  • 一个线程检查是否收到所有移动,如果他们尝试移动到重复检查
  • 检查游戏是否处于正确状态,如果是,则在数据库中的游戏行设置一个唯一的 guid。
  • 等待 x 毫秒(我已将其设置为 100)
  • 检查游戏中是否仍设置相同的 guid
  • 如果是,继续前进,如果不退出,其他人将完成此逻辑的一半,并在他们到达这里时进行模拟..

我需要一些关于这是否存在任何循环漏洞或如何改进的意见。

    private void TryToRunSimulationRound(GWDatabase db, GameModel game)
{
// if we simulate, this is going to be our id while doing it
Guid simulatorGuid = Guid.NewGuid();

// force reload of game record
db.Entry(game).Reload();

// is game in right state, and have all moves been received? if so kick it in to gear!
if (game.GameState == GameState.ActiveWaitingForMoves && game.Players.Any(x => x.NextMoves == null || x.NextMoves.Count() == 0) == false)
{
// set simulation id
game.SimulatorGuid = simulatorGuid;
game.GameState = GameState.ActiveWaitingForSimulation;
db.SaveChanges();

// wait to see if someone else might be simulating
Thread.Sleep(100);

// get a new copy of the game to check against
db.Entry(game).Reload();

// if we still have same guid on the simulator then we can go ahead.. otherwise stop, someone else is running.
// this will allow the later thread to do the simulation while the earlier thread will skip it.
if (simulatorGuid == game.SimulatorGuid && game.GameState == GameState.ActiveWaitingForSimulation)
{
var s = new SimulationHandler();

s.RunSimulation(db, game.Id);

game.SimulatorGuid = null;

// wait a little in the end just to make sure we dont have any slow threads just getting to the check...
Thread.Sleep(100);
db.SaveChanges();
}
else
{
GeneralHelpers.AddLog(db, game, null, GameLogType.Debug, "Duplicate simulations stopped, game: " + game.Id);
}
}

附言这个错误花了我很长时间才弄清楚,直到我运行了一个包含 5000 个查询的 System.Threading.Tasks.Parallel.ForEach,我每次都可以重现它。这种情况在现实世界中可能永远不会发生,但这是一个业余项目,玩起来很有趣;)

更新这是 Juan 回答后的更新代码。这也阻止了错误的发生,并且似乎是一个更干净的解决方案。

    private static readonly object _SimulationLock = new object();

private void TryToRunSimulationRound(GWDatabase db, GameModel game)
{
bool runSimulation = false;
lock (_SimulationLock) //Ensure that only 1 thread checks to run at a time.
{
// force reload of game record
db.Entry(game).Reload();

if (game.GameState == GameState.ActiveWaitingForMoves
&& game.Players.Any(x => x.NextMoves == null || x.NextMoves.Count() == 0) == false)
{
game.GameState = GameState.ActiveWaitingForSimulation;
db.SaveChanges();

runSimulation = true;
}
}

if(runSimulation){
// get a new copy of the game to check against
db.Entry(game).Reload();

if (game.GameState == GameState.ActiveWaitingForSimulation)
{
var s = new SimulationHandler();
s.RunSimulation(db, game.Id);
db.SaveChanges();
}
}
}

最佳答案

根据您的代码,如果在这种情况下两个线程同时访问:

if (game.GameState == GameState.ActiveWaitingForMoves &&  game.Players.Any(x => x.NextMoves == null || x.NextMoves.Count() == 0) == false)

两者都会尝试设置模拟 ID,并同时操作状态,这是不好的。

game.SimulatorGuid = simulatorGuid; //thread 1 is setting this, while t2 is doing that too.
game.GameState = GameState.ActiveWaitingForSimulation;
db.SaveChanges(); //which thread wins?

有一个非常好的模式可以防止这些竞争条件,而无需使用 process.sleep。如果您只需要对数据库进行原子访问(原子操作确保所有操作都按该顺序完成,没有竞争条件或来自其他线程的干扰),它还可以避免用模糊条件填充代码的需要。

可以通过跨线程共享静态对象,并使用强制执行原子操作的内置锁定机制来解决:

private static readonly object SimulationLock= new object();

然后用锁定安全预防措施包围您的代码,

`private void AtomicRunSimulationRound(GWDatabase db, GameModel game)
{
lock(SimulationLock) //Ensure that only 1 thread has access to the method at once
{
TryToRunSimulationRound(db, game);
}
}

private void TryToRunSimulationRound(GWDatabase db, GameModel game)
{
//Thread.sleep is not needed anymore
}`

还有更优雅的解决方案。与其等待资源被释放,我更愿意通过锁自动完成游戏状态检查和 ActiveWaitingForSimulation 标志的设置,然后返回一个错误,表明其他线程已经在进行模拟检查和访问那个标志,因为那将是一个原子操作,并且一次完成一个。

关于c# - 通过并行调用 WCF 避免相同的代码运行两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30631410/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com