4
\$\begingroup\$

I developing a simple real-time strategy game in a small team with no experience, and we're considering using one thread per game object (tank, soldier, etc). Is this a good idea or is it overkill?

The thread would call each object on each instant change (game timer, not graphic engine timer)

We're using Python with the Panda3D engine, meaning the GIL is probably going to reduce a lot the possible speed improvements.

\$\endgroup\$
  • \$\begingroup\$ It is an opining-based question which is not welcome on this site. My opinion is that it is tempting to create a multithreaded setup, but you will most likely won't gain anything unless you know exactly that you need to do this for various reasons (like you already have graphics, which is the usual performance-hogger but most time is spent on updating the units). That is especially the case with small team, no experience - better start simple. \$\endgroup\$ – János Turánszki Mar 21 '14 at 23:29
  • \$\begingroup\$ gamedev.stackexchange.com/questions/7338/… \$\endgroup\$ – MichaelHouse Mar 22 '14 at 15:02
  • \$\begingroup\$ @JánosTuránszki It is not really opinion based. It is pretty much impossible on today's most advance gaming rigs, let alone your average pc. \$\endgroup\$ – wolfdawn Mar 22 '14 at 15:27
  • \$\begingroup\$ @ArthurWulfWhite But we don't know anything about his program, he might only have two units in the game, then it would be possible to use threads (but maybe completely unnecessary). \$\endgroup\$ – János Turánszki Mar 22 '14 at 16:04
  • 1
    \$\begingroup\$ @JánosTuránszki I agree; Since we don't know anything we need to take the information we do have (the user is interested in making an RTS) and assume the rest of the parameters could range in value anywhere within reason because the answers are supposed to be reflect information useful to anyone who reads them in the future (who has a similar question). So it would work if you are planning to have a handful units is "somewhat" correct but I believe it is too specific and useful to a very small margin of the indie/hobbyist RTS games developers population. \$\endgroup\$ – wolfdawn Mar 22 '14 at 16:14
13
\$\begingroup\$

Rule number one of multithreading: Don't even think about it, unless you really need to use multiple CPU cores for performance reasons*. Multithreading opens up a whole can of worms of obscure and impossible to reproduce bugs:

  • Race conditions! Because you have no control over the thread scheduling of the OS, you have no control over the order in which things happen. You might assume that a certain task takes much longer to complete than another and build on that assumption. But in one of a million executions that assumption is wrong, and an obscure bug happens which is impossible to reproduce.
  • Synchronization issues! When one thread modifies data while another thread is in the process of reading it, the thread reads a mixed state of old and new data. This can lead to really obscure bugs which are - again - almost impossible to reproduce.
  • Deadlocks! The two problems above can be avoided by certain synchronization and locking techniques which are available in most programming languages. Unfortunately these features take a lot of knowledge and experience to use correctly. When you use them incorrectly, you will quickly run into deadlocks: Two threads both blocking their data structure and both waiting for the other thread to release theirs. This will lead to an infinite loop and cause both threads to freeze.

When you and your team are inexperienced, do it without multithreading. You will run into enough bugs and architecture problems without the additional complexity nightmares brought to you by unnecessary threading.

The great age of real-time strategy games were the late 90s in which there were practically no multi-core CPUs on the consumer market. Tons of great RTS games (Command&Conquer, Age of Empires, Total Annihilation, Starcraft...) did well without multiple cores on much worse hardware than you have today. So why shouldn't you be able to do the same?

*) Exception from this rule: Waiting for I/O of files, network or user input. But in that case you shouldn't write your own multithreading code. Use the asynchronous APIs with callbacks provided by the standard library when you can. These use multithreading "under the hood", but hide the nasty details from you.

\$\endgroup\$
  • 2
    \$\begingroup\$ Another important exception is GUI where heavy lifting has to run in the main thread (I guess it's similar to the generic I/O) case. If you have GUI and and some heavy number crunching running on the same thread the GUI will appear to "freeze" and your users will be confused. \$\endgroup\$ – wolfdawn Mar 22 '14 at 14:44
4
\$\begingroup\$

That sounds like a really bad idea. For multithreading to work well you need individual tasks that require significant computation, and can be easily segregated so that they don't work on the same dataset.

There is an overhead to each new thread you use, there is in some cases a big overhead for shipping data between threads, and once you have occupied each virtual core with a thread there is no performance to gain by starting more threads.

If you are to use multithreading you would want to do something like putting pathfinding in its own thread. But for now you are probably better off just making the game as simple as possible.

\$\endgroup\$
4
\$\begingroup\$

Before you do anything read the first paragraph here

First and foremost, running hundreds of threads isn't going to boost performance. It is going to instantly ruin your game. Why ? Because afaik in Windows and Linux the schedulers wakes up every millisecond (possibly more like 3) to check if another process needs to run. So if you have 50 tanks and 100 soldiers that means that you can hope for 7 frames per second. Sounds great to you?

based on this link

Each tool has advantages and disadvantages. If you don't need the advantages, you still suffer from the disadvantages if you misuse it needlessly.

tl;dr Basically, threading is "Would you do that for me? But don't stop doing this while you do that other thing. It is also sometimes, "Would you do that big task faster if I break down into smaller separable chunks?"

Multithreading is advantageous for:

  • Software performance optimization for multi-core systems hardware. I did not see a case where that was needed for indie gaming.
  • Writing software that handles multiple, possibly unrelated processes at the same time, like a server handling an unknown number of clients. This use is not as popular nowadays but it's useful to understand as a design concept.
  • Running another process asynchronously without halting the main program until it's completion. For instance, lets say you have a huge DB and you are loading some statistics. You wouldn't want the GUI to freeze until it's done? Another example that was mentioned here is running a long AI process and allowing the game to continue uninterrupted.

All the above works great especially when you don't need to share data between threads although if you do, there are special variations of the usual data-structures like lists and hashes for you to do so.

It is a subtle difference but the game is one process and the units are one part (entities) of that game that continuously need to communicate with the game. They don't need to be running on their own. They need to be synchronized with the game world.

If you want to mess around with "advanced techniques", you could try the Entity Component design pattern. I don't recommend it if you are new to this. Just use an array of soldiers and possibly pool and reuse dead soldiers. Then when you have a list or whatever container you choose, iterate over the soldiers each frame and go unit.doYourThing();

You may want all the units to inherit from a base unit so you could iterate over them all easily without having to treat each unit type differently in the main game loop.

\$\endgroup\$
4
\$\begingroup\$

I won't repeat why you should not use one thread per game entity, but if you truly want to use a simple-to-think-yet-efficient concurrent model, look for the actor model. In the actor model, you software is made of actors. Each actor is a parallel entity that you may think as a thread with state, even though it may be implemented differently. Each actor also has a mailbox which can receives messages from other actors. Thus, no state is shared between actors; every relevant information is communicated through actors by copying it into the messages. Thus in the actor model, you completely avoid inconsistent state and errors due to race conditions or bad handling of synchronization. You also avoid almost all deadlocks, which arises only by abusing the possibility for an actor to wait a reply of another actor and do nothing until then (which is bad if the actor could do something else while waiting for the reply).

In practice, actors are often written using a thread pool where a thread pool job terminates when the actor polls its mailbox for new messages. There are several frameworks in various programming languages for the actor models, even languages design around the concept (erlang). Just have a look on wikipedia : https://en.wikipedia.org/wiki/Actor_model#Actor_libraries_and_frameworks

\$\endgroup\$
  • \$\begingroup\$ Thank you, this is a very interesting approach. Have you successfully implemented it in game development? \$\endgroup\$ – bgusach Mar 24 '14 at 12:39
  • \$\begingroup\$ Only for small educative projects so far. But I didn't implemented a full-game so far although I plan to use the actor model for my current project, but I'm not at that level yet. \$\endgroup\$ – Lærne Mar 25 '14 at 6:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.