Well, what other types and how many threads should a game have?
Before thinking about multi-threading, you should reason about your memory-accessing patterns and if they are optimized enough. One thread with optimized/proper cache usage can often outweigh many threads using sloppy/improper cache usage.
Currently, I can think of no less than 4 distinct purposes/methods for multi-threading in games, and I am sure others can think of more.
1) Real-time processing. For faster response to input, Win32 messages, and the like, this processing often takes place in another thread. These threads usually require normal/high priority for fast response, although they are not active very long at a time (they sleep a lot due to inactivity).
2) Background processing. Asynchronous file loading, for instance, requires little processing power since this is an I/O-bound process. Asynchronous resource loading, on the other hand, may require a great deal of processing power when there are resources to load (and loading is not I/O-bound). But none of these tasks have to be carried out right away, so they usually have low/normal priority and will be processed when there are time to spare.
3) Scalable parallel processing. This can be rendering, physics simulation, logic processing, or any other task that can be divided into multiple smaller independent tasks, typically using task schedulers/pools (for queuing and delegation of tasks to worker threads), dependencies (tasks depending on other tasks or frame dependencies) and priorities for choosing the right order of processing. There is typically one global task scheduler and N+C worker threads, where N is the number of available hardware threads and C is some number for balancing the workload of worker threads (I have seen both -2, -1, 0 and 1, depending on the amount of processing the other threads have to perform). For reference, see http://threadingbuildingblocks.org/
4) Another popular solution for systems with few hardware threads (2-4) is to dedicate each thread to one/more subsystems and is often good enough when developing for a platform with a specific amount of threads (consoles, some mobiles, for instance). So for a 2-thread system you might have input, rendering and networking on one thread and physics and logic on another, but often one or more threads will have finished processing the current frame before the others and will have to wait (maybe not at first because it can process the next frame, but it cannot continue beyond X frames). This method can therefore lead to wasted cycles in waiting threads and is not scalable in itself.
Task schedulers, as explained in 3), address this problem by balancing the load amongst the available worker threads in order to avoid stalling any threads = no/less wasted cycles. This requires the work to be split into more-or-less equal-sized and independent tasks for optimal balancing and therefore optimum performance, though, and is therefore easiest to accomplish with something like physics, rendering and (depending on the management) logic.
I did not dive into the impact of different synchronization strategies, as others have already mentioned this.
Answer: A mix of all 4 methods is probably not a bad idea, but the answer should always be "it depends". You should consider the reason for each thread, its responsibilities, its impact on OS thread scheduling, and if the extra synchronization required, if any, is outweighed by the performance gains, also if any.
Edited by nife87, 27 December 2012 - 03:58 PM.