In my blunt opinion this is like a ridiculous way to use threads because what does user input involve? It involves checking to see if the user pressed/release some key on the keyboard or pushed/release some button on a mouse or joystick (and normally I wouldn't use a blocking function like
getch for this). Which means the thread has little work to do besides just waiting for an event/interrupt to occur or, worse, just polling and doing nothing until the user actually does the something with his/her input devices. And it's not such a big deal if a thread is mostly sleeping and doesn't have much to do, but it becomes a bigger deal if there's a latency involved in communicating from one thread to the next when a very real-time, consistently-timed response is needed.
That's just not very hefty for a dedicated thread to devote itself to doing while the latency requirements are very steep. Moreover in this design case, you don't necessarily want user input to be asynchronous of what the user is actually seeing displayed on the screen. If the user hits the contra code on his joystick/keypad really rapidly, then you don't want the display to lag behind the response of pressing those buttons.
This is very specific to a game development context but input response and display response aren't necessarily something you want to be so independent of each other in a real-time game (it's not necessarily that useful to be able to handle 1000 button pushes per second in a game like this that only displays 60 frames per second). If the frame rate is slow, that's something to fix on its own (because it's hardly practical for a user to play, say, a platformer if he can't see what he's doing fast enough), but not something to necessarily try to enhance by making input asynchronous of display/output. It's not necessarily that useful in a game to allow the user to do things in the middle of seeing the output of what he has done so far. In games like Snake it's very important to keep the frame rates interactive, and when the output is interactive, then there's not a practical need to devote a separate thread for user input. The formula of the 'main loop' common in games of this sort as they were written historically are more than adequate for the task and arguably even ideal for the task. If you want to use state-of-the-art graphics then the main loop still isn't necessarily obsolete if you can devote external threads to the heftier aspects of computation (and that doesn't include a thread dedicated to user input).
Take a classic game like Super Mario Bros. If you were to port to modern hardware and separate input handling in a separate thread from rendering, then chances are that it would feel no better or very possibly even worse when the control input response is no longer so precisely and consistently timed to frame output. It's either going to feel as right or worse (as though the frame display is somewhat a bit off from what the user is pushing on his gamepad) since we're basically just separating a thread doing very little only to communicate the results of what the user inputs to other threads just to basically cause Mario to jump or something of this sort. Even if the cost of that can be made dirt cheap from the time a button is pushed to starting to show the results of that in a frame, it's still jumping through hurdles for no real practical reason when you can make a game like this very interactive while the basic frame output and user input are being done in the same thread*.
- This is not to suggest that you do very hefty work like manually rotating huge sprites on CPU in the same thread as opposed to distributing that work across a parallel loop, for example. It's just an argument in favor of tying the task responsible for, say, blitting the final results to the frame buffer together with the task responsible for detecting user input together in favor of games that "feel right". For GUI environments there are far stronger arguments against separating the event processing thread from the GUI output thread due to the complexities of getting such implementations correct and not just responsive.
So this is somewhat controversial, maybe, and I'm afraid I can't get so scientific here in favor of "feeling right" for a game, but it's hard to beat the response and interactivity of old school games like Super Mario which didn't separate an external thread just to detect what button a user pushed. It's not something that's "enhanced" with separate threads (and even from a productivity standpoint) and I could very easily see cases where the end product "feels" worse in terms of control response. In the interest of keeping the control closely tied to the frame rates as much as possible in a video game context and given that input is hardly expensive as far as computation (including async polling if it gets to that which isn't even that bad in a game that has to constantly utilize CPU to keep up a frame rate of 60+ FPS anyway), I'd suggest to generally favor putting the "raw output" and "raw input" code together in the same thread and favor non-blocking input functions or events (I'd avoid any blocking input function like
getch like the plague for anything but a turn-based game which can wait on the user to do something before displaying the output for the next move).
The simplicity of the game might also factor in here. If we're talking Snake that might be a bit different from Unreal Engine 4 (and no sane developer should apply the same engineering practices for both if he's interested in shipping), though I'd be surprised if the developers at Epic dedicated a separate thread just for detecting user input that's completely separate from any sort of frame output.
If you have hefty computations to make in between those like checking for collision detection for physics, then you might devote a thread to that sort of thing which might have to do very hefty work for every single frame displayed. But user input is something I'd generally lean against dedicating a thread towards, and even more so in a multitasking GUI environment as opposed to like a fullscreen game.
Of course if you have like a slider which drags some integer value and each change to that integer requires some very hefty computation in the middle before an external output can be displayed, that might be something to seek to devote to a separate thread. This way the user can drag and see the integer values update in the GUI output without sacrificing interactivity of the GUI control while the external output sort of lags behind. But there the display of the control output and the user input for dragging the control with the mouse are still synchronized together on a basic level, it's just this kind of external output which requires more hefty computation which is being done asynchronously (to some extent or another) in a separate thread to avoid reducing the interactivity of the GUI input/output thread. That's a sort of design for a "pseudo-realtime" application that isn't fast enough (and together in a single thread) to output at interactive frame rates, so you keep the GUI input/output fast enough (say 30+ FPS) while the external output might only display at 1 frame per second. It's not a practical design for a video game. In a video game you want the display of all relevant output at a fast enough frame rate, at which point it starts to become impractical to devote a separate thread to user input.
All right, just to contradict myself a bit for exceptional cases and yield to any who might disagree with this opinion, I could see more of a case for a separate user input thread in even a simple typing game: those games where you have to type what you see on screen as fast as possible. In those cases the nature of the game input might not be so directly tied to frame output. Even if the game can display its frames at 30+ FPS, a user super fast on the keyboard might type faster than this or hit two or more keys in between the display of one frame to the next, and there it might be useful to queue up those results in some producer thread and have a consumer pop it off and handle all the keys pressed in between those two frames. That's a very different game from Snake though or Super Mario or something where it's not necessarily useful to queue up what the user has pressed in between frames (those games tend to benefit more from just knowing when a button is down/up for a particular frame as opposed to queuing up key presses like an input field). It's like if the user presses down and then up key in between one frame to another, there's nothing for the game to do except ignore one of those two keys and start moving the snake in one of those directions when it comes time to handle the game logic and display the frame. I'm taking very much into account the specific nature of the type of game being developed in my answer and also what I believe is the simplest, most practical solution for such an application that's likely to yield desired results with minimal fuss.