【问题标题】:Are there best practices for implementing an asynchronous game engine loop?是否有实现异步游戏引擎循环的最佳实践?
【发布时间】:2011-01-02 03:33:17
【问题描述】:

我已经实现了一个游戏引擎循环如下:

public static Boolean Start ( )
{
    if (hasBoard)
    {
        //  start engine on worker thread
        asyncTask = new AsyncResult ( stopEngine, asyncTask );
        isRunning = ThreadPool.QueueUserWorkItem ( startEngine, asyncTask );

        if (isRunning)
        {
            Console.WriteLine ( "[{0}] Engine started",
                DateTime.Now.ToString ( "hh:mm:ss" ) );
        }
        else
        {
            Console.WriteLine ( "[{0}] Engine failed to start",
                DateTime.Now.ToString ( "hh:mm:ss" ) );
        }
    }

    return isRunning;
}

public static void Stop ( )
{
    Console.WriteLine ( "[{0}] Engine stopping",
        DateTime.Now.ToString ( "hh:mm:ss" ) );

    asyncTask.SetAsCompleted ( null, false );
}

private static void startEngine ( Object task )
{
    while (!( (IAsyncResult)task ).IsCompleted)
    {
        Thread.Sleep ( 10000 );

        Console.WriteLine ( "[{0}] Engine running",
            DateTime.Now.ToString ( "hh:mm:ss" ) );
    }
}

private static void stopEngine ( IAsyncResult iaResult )
{
    //  clean up resources

    Console.WriteLine ( "[{0}] Engine stopped", 
        DateTime.Now.ToString ( "hh:mm:ss" ) );

    isRunning = false;
}

我正在使用 Jeff Richter 在他的文章 Implementing the CLR Asynchronous Programming Model 中推荐的 AsyncResult 类。为了能够从 UI 中停止引擎,我使用的实现有点偏离标准异步模式。此实现按预期工作,但是当我偏离标准实践时,我会回到 SO 社区以确保我以正确的方式做事。

此实现是否存在任何人都可以看到的问题?

【问题讨论】:

  • 在游戏引擎循环中,您不会简单地生成一个新线程而不是使用池线程吗?
  • @Mitch:我不太确定。我相信产生一个新线程是标准的。我不知道使用 Threadpool 线程有什么问题。这个游戏引擎是超轻量级的。此外,由于 WinForms UI 在控制中,我认为这是处理游戏循环的好方法。
  • 你为什么在游戏循环中使用 any 线程?如果您需要任何(这确实应该受到很好的质疑),线程应该可能是为特定明确目的预先生成的线程(例如,总是2个线程等. 连续生活)在队列或其他同步中工作。然而,最好的办法就是保持“KISS”。
  • @pst:可能是因为这是我第一次尝试构建和理解一个非常简单的游戏引擎。

标签: c# .net asynchronous game-loop


【解决方案1】:

由于这听起来像是一个您可以控制的项目,我建议您放弃 APM 并使用 .NET4 中提供的基于 Task 的模型。这是 .NET4 而不是 APM 的推荐方法。 Task 类是 Task Parallel Library (TPL) 的一部分,但它也适用于这些基本的异步作业。

private CancellationTokenSource cts;

public void StartEngine()
{
    if (cts == null)
    {
        cts = new CancellationTokenSource();
        Task.Factory.StartNew(() => GameLoop(cts.Token), cts.Token);
    }
}

private void GameLoop(CancellationToken token)
{
    while (true)
    {
        token.ThrowIfCancellationRequested();
        Thread.Sleep(1000);
        Debug.WriteLine("working...");
    }
}

public void StopEngine()
{
    if (cts != null)
    {
        cts.Cancel();
        cts = null;
    }
}

【讨论】:

  • 同意,但我会在创建任务时添加一个 LongRunning 选项。
  • 我进行了修改以添加 LongRunning 选项:Task.Factory.StartNew ( ( ) => startEngine ( cancelSource.Token ), cancelSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default );
【解决方案2】:

我会说,这意味着使用线程池时任务是短暂的,因为其中的线程数量是有限的。

在你的情况下,我会使用 BackgroundWorker,因为你提到了 winforms。 BW 为您处理同步,因此无需使用 InvokeRequired 等即可更新 GUI。

【讨论】:

  • 暗示使用线程池时任务是短暂的 - 根据 Jeff Richter 的说法……除非我真的错过了什么。但是,我确实看到了保持线程池线程任务简短的观点。
  • @IAbstract:你有这方面的参考吗?读起来会很有趣。
  • 我将不得不再次找到它 - 自从我发表评论以来已经一年半多了。任务寿命短的含义可能是由于默认处理 - 即,如果我们预计执行时间长,我们必须将任务设置为长时间运行。我评论的内容一定是有意义的,但我会尝试找到我所指的内容。 ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-10
相关资源
最近更新 更多