是否有实现异步游戏引擎循环的最佳实践?

Posted

技术标签:

【中文标题】是否有实现异步游戏引擎循环的最佳实践?【英文标题】: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:可能是因为这是我第一次尝试构建和理解一个非常简单的游戏引擎。 【参考方案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:你有这方面的参考吗?读起来会很有趣。 我将不得不再次找到它 - 自从我发表评论以来已经一年半多了。任务寿命短的含义可能是由于默认处理 - 即,如果我们预计执行时间长,我们必须将任务设置为长时间运行。我评论的内容一定是有意义的,但我会尝试找到我所指的内容。 ;)

以上是关于是否有实现异步游戏引擎循环的最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章

是否有在 C++ 中循环“一直通过”数组的最佳实践?

在 Android 中发出异步 HTTP 请求是不是有公认的最佳实践?

使用 fetch 在节点中进行异步 api 调用的最佳实践?

基于Flink+ClickHouse构建实时游戏数据分析最佳实践

Celery学习--- Celery 最佳实践之与django结合实现异步任务

结合异步模型,再次总结Netty多线程编码最佳实践