c# - 锁定列表会产生死锁
问题描述
我正在开发一个使用multi-threading的C# WPF游戏,我遇到了一个错误,当我向操场添加一个新实体时,游戏会突然停止。
我有 2 个任务运行每个刻度,使游戏正常运行。第一个是进行所有计算并运行模拟的那个。第二个用于在每个滴答中更新渲染器。
此代码在游戏开始时运行:
Task.Run(() =>
{
while (state != GameLevelState.Ended)
{
Thread.Sleep(1000 / 60);
if (state == GameLevelState.Paused)
continue;
Task.Run(RunSimulation);
Task.Run(UpdateRenderer);
}
});
问题从存储它们的实体列表开始,因为我需要使用我的两个任务来访问它,而且我还需要在单击按钮时创建一个新实体。当 UI 线程想要检查实体 List 中是否已经存在另一个相同类型的实体时,就会出现死锁。
这是获取实体的代码:
public List<GameEntity> GetGameEntities(Func<GameEntity, bool> predicate = null)
{
if (predicate == null)
predicate = (GameEntity e) => true;
List<GameEntity> selected = new List<GameEntity>();
// this is where the execution stops
lock (Entities)
{
foreach (var entityList in Entities)
selected.AddRange(entityList.Value.Where(predicate));
return selected;
}
}
因此,当我在添加实体时尝试获取相同类型的实体时,执行会在锁上停止。我的理论是 UI 线程找不到开口,因为其他 2 个线程/任务不断锁定实体列表。(他们只在迭代列表时锁定列表)
(我还应该指出,如果不理会这两个任务/线程基本上永远完美运行。它只会在尝试从 UI 线程创建/添加实体时停止)
以下是任务执行的相关代码:
私人无效运行模拟()
/* ... */
lock (Entities)
{
foreach (var entityList in Entities.ToArray())
foreach (GameEntity entity in entityList.Value.ToArray())
if (!entity.Tick())
{
entityList.Value.Remove(entity);
Application.Current.Dispatcher.Invoke(() => renderer.RemoveEntity(entity));
}
}
/* ... */
私人无效更新渲染器()
/* ... */
lock (Entities)
{
foreach (var entityList in Entities)
foreach (GameEntity entity in entityList.Value.ToArray())
Application.Current.Dispatcher.Invoke(() => renderer.DrawEntity(entity));
}
/* ... */
我的问题是:我该如何解决这个问题?
我是否应该制作 List 的副本并遍历它,以便 List 仅在复制时被锁定?或者我应该将实体添加过程委托给执行所有计算的线程吗?
任何帮助是极大的赞赏!
解决方案
不确定这是否能解决您的问题,但它肯定有助于控制您生成的任务。在当前正在运行的任务完成之前,不会启动新的循环。它使用await
andTask.Delay
而不是Thread.Sleep
.
Task.Run(async () =>
{
while (state != GameLevelState.Ended)
{
var delayTask = Task.Delay(1000 / 60);
if (state != GameLevelState.Paused)
{
var task1 = Task.Run(RunSimulation);
var task2 = Task.Run(UpdateRenderer);
await Task.WhenAll(task1, task2);
}
await delayTask;
}
});
推荐阅读
- c# - 如何使用参数运行现有的计划任务?
- java - Log4j2 在 tomcat 的共享类路径中记录类
- python - pip 和 python 的路径不同步?
- python - 如何强制 WAMP 票证身份验证?
- android - 如果我通过 WiFi-Direct 连接 2 台安卓设备 - 一台可以与另一台共享其 Internet 连接吗?
- aem - 在 AEM 6.3 CFP2 中,更改/删除的属性不再冒泡吗?
- vba - Vlookup 语法和 MsgBox 错误
- ajax - 授权标头不适用于 cors
- python - 将 JSONL 文件加载为 JSON 对象
- python - 使用 `numpy.vectorize` 创建多维数组会导致 ValueError: setting an array element with a sequence