3

I've created a game in VS2010 using VB .NET 4 in C#. The game creates a thread for each player, and runs LUA code that the players have created.

Each LUA code is blocking for the thread, and should only terminate when the thread is terminated (The LUA script should contain an infinite loop)

I want to limit the CPU usage as the computer is currently overheating after running with 100% CPU for a few hours.

How can I throttle the threads without having control within them? I was hoping to have a thread manager to force suspend/resume but those are becoming obsolete, and I don't want to be throwing exceptions because that will abort my LUA code that's executing.

Any ideas?

Thanks!

pcaston2
  • 421
  • 1
  • 6
  • 17
  • 1
    Can you have your C-Sharp code call a method which is essentially your main loop? I'm not sure if LUA has a high-resolution timer (which I'm assuming you're using for your game logic), but C-Sharp definitely does, so your main method in LUA may accept the delta between frames instead of calculating it itself. – Matthew Aug 08 '12 at 20:42
  • The way it currently works is that all game logic is done in C-Sharp. the LUA threads are able to get information about the game, and return control information to the game, by way of .NET functions that can be called in the LUA as well (a kind of function mapping). I'm not sure I understand what you're asking, does this help? – pcaston2 Aug 09 '12 at 13:51
  • I was under the impression that the LUA would do the majority of the game logic. What does LUA add if the C-Sharp does the logic? – Matthew Aug 09 '12 at 14:47
  • Think of the LUA as the controllers for the players. The game continues to happen, and the LUA code the players submitted will observe and control the players they're associated to in the game. – pcaston2 Aug 09 '12 at 15:35
  • For me, I would try and have all the logic in one place (language), that way you don't have to worry about syncronizing and marshalling the data. – Matthew Aug 09 '12 at 15:55
  • I went with LUA because it's often used for third-party add-ons. I could have allowed people to use C# but that would have been harder to sandbox. Regardless of the implementation, I'd probably come to the same problem. – pcaston2 Aug 09 '12 at 17:39

3 Answers3

7

You can set the thread priority to low for those threads - that would help your system remain responsive... but you'd still use 100% CPU and still overheat, so I won't go into detail of how.

As other posters have mentioned, you need to get your threads to insert small sleeps. If you have no control over them at all, you can't do anything else.

I'd suggest you need to change your game API.

Rather than having your code call the Lua code one time, and then Lua code infinite looping, I'd suggest you make the Lua code have a "Run one frame" function. You then move the infinite loop into your own code, and repeatedly call the "Run one frame" function.

That way, you can insert sleeps, add logging, and do all kinds of other useful things.

Orion Edwards
  • 121,657
  • 64
  • 239
  • 328
  • +1 for timed frames over infinite loops. Flash player does this to good effect. – spender Aug 08 '12 at 21:15
  • 1
    @spender for that matter, so does Windows. – Jonathan Henson Aug 08 '12 at 23:02
  • I like this approach to some degree, however if I am constantly interrupting the thread and rebooting it, the LUA environment is getting torn down so the users have a limited ability to maintain state. I believe what I'll do is add a function that the LUA script can call, which causes the .NET thread to sleep. I can monitor how much time has expired since the last call, and if the user is running 'hot' I can shut down the thread, sleep for a long time, and then reboot the thread. This forces the users to be reasonable about there processing I suppose. I'll give this a shot. – pcaston2 Aug 09 '12 at 14:29
  • I've added that when the user calls the method which gives the feedback (which should be once per cycle at least) there is a small sleep. So far this has improved performance drastically but doesn't help in infinite loops without calling this function. – pcaston2 Aug 09 '12 at 15:37
  • I've fixed the issue sufficiently using some ideas that were brought up here. When users call the controller function, or the sleep function (in the case where they might be doing a long process but haven't decided which action to perform) a timer is reset. The main game engine is checking this timer every run through, and if the timer exceeds a certain amount, the thread is killed then rebooted after a long sleep. Even if a player is constantly locking up, the CPU doesn't seem to undergo much strain. Now the majority of the CPU is used by the game thread, but still under 30%. Thanks so much! – pcaston2 Aug 09 '12 at 17:51
1

The typical way to make sure a thread in an infinite loop doesn't consume too much cpu is to make it sleep a little in each iteration (5 ms will usually do the trick). This will give the system time to allow other resources to use the cpu. Also, do not give threads Highest Priority.

Jonathan Henson
  • 8,076
  • 3
  • 28
  • 52
  • Not sure how helpful that would be if "LUA code is blocking for the thread". I assume that means the LUA code is executing and you can't call Thread.Sleep(). – Peter Ritchie Aug 08 '12 at 20:34
  • @Peter I was going on "The LUA script should contain an infinite loop" part. The code containing the infinite loop should call the sleep function. – Jonathan Henson Aug 08 '12 at 20:35
  • BTW. Thread.Sleep has a granularity defined by a system "quantum". Sleep(5) basically will never happen. It will likely be the equivalent of Sleep(20) depending on the system. If you have a CPU bound thread that loops, you can give up CPU to other threads with Thread.Sleep(1). If you don't have a loop or don't have specific points you can invoke Thread.Sleep, this Sleep concept won't work. – Peter Ritchie Aug 08 '12 at 20:36
  • I can't call sleep from within the thread, the LUA code is blocking, yes. I can access the threads from an engine manager that is doing the game calculations alongside the players, can this thread throttle the others? Or can I only sleep a thread from within? – pcaston2 Aug 08 '12 at 20:37
  • @Jonathan LUA is a different programming language. It doesn't have the ability to call .NET Thread.Sleep(). I don't know if it even has an ability to "sleep" a thread. If it doesn't, this concept won't work. – Peter Ritchie Aug 08 '12 at 20:38
  • @JonathanHenson I did not write the code in the LUA script, it could be anything, it's sandboxed but user-submitted. I can't reliably control whether or not they throttle there own thread. – pcaston2 Aug 08 '12 at 20:38
  • @PeterRitchie I know that, however, the operating system does have a sleep call. I would think any useful programming language would be able to call it. – Jonathan Henson Aug 08 '12 at 20:39
  • @pcaston2 gotcha, Have you tried giving the threads lowest priority when creating them? – Jonathan Henson Aug 08 '12 at 20:40
  • @JonathanHenson Yes the threads have lowest priority. The computer performs well but still idles at 100% (the 4-5 threads eat up all the CPU they can). And the LUA could call the sleep function in .Net if I programmed it too, but whether the users did or not would be up to them. – pcaston2 Aug 08 '12 at 20:41
  • @pcaston2, They don't need to use the .NET function, just the o.s. sleep call. Also, does LUA support any type of inheritance, where the compiler or interpreter could enforce the constraint of writing good infinite loop code? – Jonathan Henson Aug 08 '12 at 20:44
  • Worst case you can have external (C#) code Suspend he thread every now and then for a few timeslices. – H H Aug 08 '12 at 21:14
  • While the resume and suspend code is obsolete, I'm certainly not using any resources within my code so I suppose it would be a safe option for now. – pcaston2 Aug 09 '12 at 02:54
  • @pcaston2, I think orion's answer is the best approach. That goes along with my comment on your question. You need a way for the compiler to enforce the good coding practice. If you change your api to just be a callback or a polling function that you call in your own loop, that is the best option. – Jonathan Henson Aug 09 '12 at 04:43
1

The classic way to gain control of the scheduling of threads of execution is to use Fibers.

However, Fibers are a native feature of the WIN32 API....the nearest you can come in C# is to use iterators...where you do bits of work inbetween each yield....but you decide when you want the next bit of work to be done by calling the iterator.

See 2nd link...might be a way to use the unmanaged native Fibre API to wrap some .NET coroutines, or if the LUA code you are running in this thread is native and you don't have access to the source code...then you might be able to use ConvertThreadToFibre to gain control of it (and decide when it gets scheduled using some interop to the Fiber API calls).

Or you could have a scheduling thread which uses Events to signal to the player threads when they can continue and do some work (if some of your code is native/C++ and some is C# then each can refer to the same Event handles....just use the WIN32API or .NET API in each bit of code).

Community
  • 1
  • 1
Colin Smith
  • 12,375
  • 4
  • 39
  • 47