【问题标题】:How to create an application loop in C#如何在 C# 中创建应用程序循环
【发布时间】:2017-05-16 00:41:45
【问题描述】:

我有一个 C# 中的控制台服务器,它一直在 while(true) 循环中运行。但这需要> 50%的CPU,即使它什么都不做。我试过 Thread.Sleep 它工作!不再吃我的 CPU,但是它不会在指定的确切时间恢复,并且不被认为是好的做法。我在做正确的事吗?或者除了使用 while(true) 和 Thread.Sleep 还有其他方法吗?

【问题讨论】:

  • 循环在做什么?通常你会等待某种事件发生,而不是忙循环或使用Thread.Sleep()
  • 您希望循环在什么条件下执行或暂停?
  • 这是服务器的主循环。它将处理所有游戏逻辑。它还需要检查是否有挂起的连接。所以它不听任何事件。它触发事件。但是,如果循环没有做任何事情或负载不足,它不仅会加快游戏速度(可以处理),而且还会消耗 > 50% 的 CPU,基本上什么都不做。
  • @SamiKuhmonen 所以我想当你不听任何事件时使用 Thread.Sleep 也不错?
  • 如果它正在接受连接,那么这些是事件,应该这样处理。但当然,如果你愿意,你可以推着睡在那里。它比忙循环要好,但不如使用事件做事,事件可以是计时器或其他来源。

标签: c# multithreading loops server


【解决方案1】:

当你想在不消耗CPU资源的情况下暂停线程一段时间,你通常使用一些WaitHandle(例如AutoResetEventManualResetEvent)并调用它的WaitOne()方法来暂停线程直到事件发生唤醒它发生(例如按键被按下,新的网络连接到达,异步操作完成等)。

要定期唤醒线程,您可以使用计时器。我不知道 .NET Framework 中有任何提供 WaitHandle 的计时器(当然您可以自己轻松地创建此类),因此必须使用 Timer 并在其回调中的每个滴答声上手动调用 AutoResetEvent.Set()。

private static AutoResetEvent TimerWaitHandle = new AutoResetEvent(false);

static void Main()
{
    // Initialize timer
    var timerPeriod = TimeSpan.FromMilliseconds(500);
    Timer timer = new Timer(TimerCallback, null, timerPeriod, timerPeriod);

    while(true)
    {
        // Here perform your game logic

        // Suspend main thread until next timer's tick
        TimerWaitHandle.WaitOne();

        // It is sometimes useful to wake up thread by more than event,
        // for example when new user connects etc. WaitHandle.WaitAny()
        // allows you to wake up thread by any event, whichever occurs first.
        //WaitHandle.WaitAny(new[] { TimerWaitHandle, tcpListener.BeginAcceptSocket(...).AsyncWaitHandle });
    }
}

static void TimerCallback(Object state)
{
    // If possible, you can perform desired game logic here, but if you
    // need to handle it on main thread, wake it using TimerWaitHandle.Set()

    TimerWaitHandle.Set();
}

【讨论】:

    【解决方案2】:

    您可以使用System.Threading.Timer 类。它提供了一种机制,可以在指定的时间间隔在线程池线程上执行方法。

    例子

    public void Start()
    {
    }
    
    int dueTime = 1000;
    int periodTS = 5000;
    System.Threading.Timer myTimer = new System.Threading.Timer(new TimerCallback(Start), null, dueTime, periodTS);
    

    这将在调用它 1 秒后调用 start 方法,之后每 5 秒调用一次。

    你可以阅读更多关于Timerhere的信息。

    【讨论】:

    • 我试过了,它成功了,但是我的主线程结束执行并且程序停止了。这又需要我让线程睡觉:(
    • @KidCoder 你不能只启动一个计时器然后让主线程返回,你会发现它会退出程序。主线程可以做其他事情;或者,如果它无事可做,它可以等待计时器。一般来说,如果你打电话给Sleep,那就是设计不佳的标志。
    • 如何在没有 while 循环的情况下等待计时器?我知道 Thread.Sleep 被认为是不好的,这就是为什么我问这个问题是为了知道是否有更好的方法来做到这一点。
    • 您可以为此使用AutoResetEvent - 主线程将在while循环中调用它的WaitOne() 方法(它会暂停线程直到调用Set() 方法)并且定时器的回调将调用Set( ) 方法来定期唤醒主线程。
    • @Ňuf 我试过了,它奏效了!如果它被认为是比 Thread.Sleep 更好的做法,我已经准备好实施它。您能否将其发布为答案?
    【解决方案3】:

    我不能评论,所以我把它放在这里。

    理论上Thread.sleep(1) 不会使用那么多 CPU。 您可以从此问题/答案中获得更多信息:What is the impact of Thread.Sleep(1) in C#?

    【讨论】:

    • 是的,我不吃 CPU,但问题是:有没有更好的方法来做到这一点,或者这是我不听任何事件时的唯一方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-06
    • 2020-09-16
    • 2021-09-23
    相关资源
    最近更新 更多