我正在构建一个具有标准循环的游戏引擎(更新/绘制每秒调用60次)。我需要一个后台任务,当我的主线程忙时,它会工作。我对并发性知之甚少,以下是我可以提出的选项:
选项A:
// Runs once.
protected override void Initialize()
{
Scene.BGThreadTrigger = new ManualResetEventSlim(false);
Scene.BGTask = Task.Run((Action)Scene.BGThreadFunction);
}
// Runs 60 times per second.
protected override void Update()
{
BGThreadTrigger.Set();
}
public static ConcurrentQueue<Action> BGThreadTaskContainer = new ConcurrentQueue<Action>();
public static Task BGEventWait()
{
BGThreadTrigger.Wait();
return Task.FromResult(false);
}
public static async void BGThreadFunction()
{
Action action;
while (ZHiGame.GameIsRunning && SceneIsAlive)
{
await BGEventWait();
while (BGThreadTaskContainer.TryDequeue(out action))
action();
BGThreadTrigger.Reset();
}
}
选项B:
public static ConcurrentQueue<Action> BGThreadTaskContainer = new ConcurrentQueue<Action>();
// Runs 60 times per second.
protected override void Update()
{
Scene.BGTask = Task.Run((Action)Scene.BGThreadFunction);
}
public static async void BGThreadFunction()
{
Action action;
while (BGThreadTaskContainer.TryDequeue(out action))
action();
}
我想知道有经验的编码人员对并发工作的看法 - 实现这一点的最佳方式是什么。
更新:我需要在特定时间点严格执行BGThreadFunction中的代码 - 当Draw()方法执行时。我面临的限制是Draw()方法只在主线程上执行。但是有一些工作可以在执行时完成,所以我需要的是一种收集工作的方法(一个容器)以及一种在给定时间点触发它的方法。我不需要一个容器,一旦添加它们就异步执行任务,因为通常在添加它们时(我的例子,遗憾的是没有显示)所有线程都很忙,即使它们不是,我仍然需要一种方法控制容器被清空和处理的时间。
更新:收到评论后,我设法创建了以下系统:
public static ActionBlock<Action> WorkerBlock;
// Runs once.
protected override void Initialize()
{
Scene.BGThreadTrigger = new ManualResetEventSlim(false);
Scene.BGTask = Task.Run((Action)Scene.BGThreadFunction);
}
public static ConcurrentQueue<Action> BGThreadTaskContainer = new ConcurrentQueue<Action>();
// Runs 60 times per second.
protected override void Update()
{
Action action;
// This way I signal the start of the background work each frame.
while (BGThreadTaskContainer.TryDequeue(out action))
WorkerBlock.Post(action);
}
更新:我也试过这个解决方案:
Thread BGThread;
// Runs 60 times per second.
protected override async void Update()
{
if (BGThread != null)
BGThread.Wait();
BGThread = Task.Run((Action)WhileDrawIsExecuted);
}
public static void WhileDrawIsExecuted()
{
Parallel.ForEach(BGThreadTaskContainer, action =>
{
action();
});
Action completedAction;
while (BGThreadTaskContainer.TryDequeue(out completedAction)) ;
}
我认为这个是更好的,但我不确定Task.Run()是否可以安全地调用(每秒60次)。