- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在制作一个小游戏,用户可以在其中使用 WCF 发送 Action ,当游戏中的所有 Action 都已发送后,游戏会模拟下一轮。
数据存储在 MSSQL 数据库中并通过 EntityFramework 访问。
在每次调用结束时检查是否已发送所有移动以及是否应开始模拟。
这里有一个问题。如果两个玩家都在一个非常小的时间窗口内发送他们的移动,那么两个线程都有可能检查是否所有移动都已发送,发现他们已经发送并开始两个模拟,直到其中一个足够远以将状态设置为模拟(并阻止任何进一步的尝试)。
为了解决这个问题,我写了以下检查。
总体思路是这样的:
我需要一些关于这是否存在任何循环漏洞或如何改进的意见。
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/
为了让我的代码几乎完全用 Jquery 编写,我想用 Jquery 重写 AJAX 调用。 这是从网页到 Tomcat servlet 的调用。 我目前情况的类似代码: var http = new
我想使用 JNI 从 Java 调用 C 函数。在 C 函数中,我想创建一个 JVM 并调用一些 Java 对象。当我尝试创建 JVM 时,JNI_CreateJavaVM 返回 -1。 所以,我想知
环顾四周,我发现从 HTML 调用 Javascript 函数的最佳方法是将函数本身放在 HTML 中,而不是外部 Javascript 文件。所以我一直在网上四处寻找,找到了一些简短的教程,我可以根
我有这个组件: import {Component} from 'angular2/core'; import {UserServices} from '../services/UserService
我正在尝试用 C 实现一个简单的 OpenSSL 客户端/服务器模型,并且对 BIO_* 调用的使用感到好奇,与原始 SSL_* 调用相比,它允许一些不错的功能。 我对此比较陌生,所以我可能会完全错误
我正在处理有关异步调用的难题: 一个 JQuery 函数在用户点击时执行,然后调用一个 php 文件来检查用户输入是否与数据库中已有的信息重叠。如果是这样,则应提示用户确认是否要继续或取消,如果他单击
我有以下类(class)。 public Task { public static Task getInstance(String taskName) { return new
嘿,我正在构建一个小游戏,我正在通过制作一个数字 vector 来创建关卡,该数字 vector 通过枚举与 1-4 种颜色相关联。问题是循环(在 Simon::loadChallenge 中)我将颜
我有一个java spring boot api(数据接收器),客户端调用它来保存一些数据。一旦我完成了数据的持久化,我想进行另一个 api 调用(应该处理持久化的数据 - 数据聚合器),它应该自行异
首先,这涉及桌面应用程序而不是 ASP .Net 应用程序。 我已经为我的项目添加了一个 Web 引用,并构建了各种数据对象,例如 PayerInfo、Address 和 CreditCard。但问题
我如何告诉 FAKE 编译 .fs文件使用 fsc ? 解释如何传递参数的奖励积分,如 -a和 -target:dll . 编辑:我应该澄清一下,我正在尝试在没有 MSBuild/xbuild/.sl
我使用下划线模板配置了一个简单的主干模型和 View 。两个单独的 API 使用完全相同的配置。 API 1 按预期工作。 要重现该问题,请注释掉 API 1 的 URL,并取消注释 API 2 的
我不确定什么是更好的做法或更现实的做法。我希望从头开始创建目录系统,但不确定最佳方法是什么。 我想我在需要显示信息时使用对象,例如 info.php?id=100。有这样的代码用于显示 Game.cl
from datetime import timedelta class A: def __abs__(self): return -self class B1(A):
我在操作此生命游戏示例代码中的数组时遇到问题。 情况: “生命游戏”是约翰·康威发明的一种细胞自动化技术。它由一个细胞网格组成,这些细胞可以根据数学规则生存/死亡/繁殖。该网格中的活细胞和死细胞通过
如果我像这样调用 read() 来读取文件: unsigned char buf[512]; memset(buf, 0, sizeof(unsigned char) * 512); int fd;
我用 C 编写了一个简单的服务器,并希望调用它的功能与调用其他 C 守护程序的功能相同(例如使用 ./ftpd start 调用它并使用 ./ftpd stop 关闭该实例)。显然我遇到的问题是我不知
在 dos 中,当我粘贴此命令时它会起作用: "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" https://google.
在 dos 中,当我粘贴此命令时它会起作用: "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" https://google.
我希望能够从 cmd 在我的 Windows 10 计算机上调用 python3。 我已重新安装 Python3.7 以确保选择“添加到路径”选项,但仍无法调用 python3 并使 CMD 启动 P
我是一名优秀的程序员,十分优秀!