1

I have a console server in C# that keeps running in a while(true) loop. But that takes > 50% CPU even if it is doing nothing. I tried Thread.Sleep it worked! Not eating my CPU anymore but, it do not resumes in the exact time specified and is not considered good practice. Am I doing the right thing? Or is there any other way than using while(true) and Thread.Sleep?

KidCoder
  • 25
  • 6
  • 2
    What is the loop doing? Usually you'd wait for some kind of event to happen, not do a busy loop or use `Thread.Sleep()` – Sami Kuhmonen Jan 01 '17 at 18:19
  • On what conditions you want your loop to execute, or to pause? – Khalil Khalaf Jan 01 '17 at 18:50
  • That is the main loop for the server. It will handle all the game logic. It also need to check if there are pending connections. So it do not listen to any event. It triggers events. But if the loop is not doing anything or is under-loaded it not only speeds up the game too much (can handle that) but it also eat's > 50% CPU basically doing nothing. – KidCoder Jan 01 '17 at 19:08
  • @SamiKuhmonen so I guess that using Thread.Sleep isn't bad when you are not listening to any event? – KidCoder Jan 01 '17 at 19:10
  • If it's accepting connections then those are events and should be handled as such. But of course you can shove sleeps there if you want. It's better than busy loop but not as good as doing things with events, which can be timers or other sources. – Sami Kuhmonen Jan 01 '17 at 19:16
  • No they are not events! Before accepting them I need to check if connections are pending by TcpListener.Pending() then I can accept a socket. There are no events that would trigger my main method. My main method that is listening to request triggers all the events. That's why it is running in a while loop. So I guess I need to stick to Thread.Sleep(1) right? – KidCoder Jan 01 '17 at 19:24
  • TcpListener actualy has (kind of) event when new connection is pending - pass desired delegate to BeginAcceptSocket() method. Similarly you can use AcceptSocketAsync() method, which basicaly do the same. Or start new thread that will call AcceptSocket() in infinite loop - such thread will not consume any CPU resources while waiting for new incoming connection. – Ňuf Jan 01 '17 at 20:11
  • I can do that @Ňuf but the main thread still needs to run to run the game. If I listener I can stop high CPU usage till 1 user connects but after then when I am processing, the CPU will consume as much resources as possible in a while true loop. – KidCoder Jan 02 '17 at 13:54
  • @KidCoder What do you mean when you say, "it do not resumes in the exact time specified"? You say to wait 100 milliseconds and it takes 200 milliseconds? If you wait 1 millisecond and then let it do something else it has to wait until the other tasks are finished. Try to set it at the frame rate so 15 or 30 ms if it makes sense. Also, who said it is bad practice? Can you post your code? – 1.21 gigawatts May 15 '21 at 10:42
  • @KidCoder I found out why not to use Thread.Sleep(). It blocks the UI thread. https://stackoverflow.com/questions/12039695/thread-sleep-in-c-sharp – 1.21 gigawatts May 15 '21 at 11:04

3 Answers3

0

I can't comment, so i'll put it here.

Theorically with Thread.sleep(1) it won't use that much CPU. You can get more info from this question/answer: What is the impact of Thread.Sleep(1) in C#?

Community
  • 1
  • 1
Edgar Luque
  • 388
  • 1
  • 4
  • 14
  • Yes, I does not eat the CPU, but the question is: is there any better way to do that or that is the only way when I am not listening to any event. – KidCoder Jan 01 '17 at 19:11
0

You can use System.Threading.Timer class. It Provides a mechanism for executing a method on a thread pool thread at specified intervals.

Example

public void Start()
{
}

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

This will call start method after 1 second from calling it and after that start will be called after every 5 second.

You can read more about Timer class here.

Ahmar
  • 3,717
  • 2
  • 24
  • 42
  • I tried it and it worked but my main thread ended executing and the program stopped. Which again requires me to sleep the thread :( – KidCoder Jan 01 '17 at 19:21
  • @KidCoder You can't just start a timer and then have the main thread return, that will exit the program as you found out.The main thread can be doing other things; or, if it has nothing to do, it could wait on the timer. Generally if you're calling `Sleep` that's a sign of poor design. – 404 Jan 01 '17 at 22:06
  • How can I wait for a timer without a while loop? I know Thread.Sleep is considered bad that's why I asked this question to know if there is a better way to do that. – KidCoder Jan 02 '17 at 13:54
  • You can use [AutoResetEvent](https://msdn.microsoft.com/en-us/library/system.threading.autoresetevent(v=vs.110).aspx) for this - main thread will call it's WaitOne() method inside while-loop (it will pause thread until Set() method is called) and Timer's callback will call Set() method to wake up main thread periodically. – Ňuf Jan 02 '17 at 15:07
  • @Ňuf I tried it and it worked! If it's considered a better practice than Thread.Sleep I am ready to implement it. Could you please post it as an answer? – KidCoder Jan 05 '17 at 18:00
0

When you want to suspend thread for a while without consuming CPU resources, you usually use some WaitHandle (such as AutoResetEvent or ManualResetEvent) and call it's WaitOne() method to suspend thread until event that is supposed to wake it up occurs (e.g. key is pressed, new network connection arrives, asynchronous operation finishes, etc.).

To wake up thread periodically, you can use timer. I'm not aware of any timer in .NET Framework, that provides WaitHandle (of course you can easily create such class yourself), so have to use Timer and call AutoResetEvent.Set() manually on each tick in it's callback.

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();
}
Ňuf
  • 6,027
  • 2
  • 23
  • 26