1
\$\begingroup\$

What are the pros/cons of using either C++20 std:async or C++/WinRT's asynchronous thread and messaging support?

What would the code look like when implementing multithreaded game-loops that interoperate through asynchronous messaging?

Basically, I am considering starting up a bunch of different system component threads which would then each manage their own different multithreaded sub-jobs, (components like the UI, Audio, Chat, Network, World, etc). But regardless of how this can be designed, I would like to understand the different usages of the APIs to use asynchronous messaging for interoperation between system threads.

Most game-loops are a bit different than this model in that they have a tight loop tied to framerate, etc. Although this still applies for UI components, it seems more ideal to separate the other game components from this loop.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Multithreading in games these days is usually less about "put each system on its own dedicated thread" and more "divide each system into batches of jobs that can be farmed out in parallel to a collection of workers". I discuss a bit in this answer why this tends to scale better, particularly with systems that tightly couple to others, like gameplay, physics, UI. \$\endgroup\$
    – DMGregory
    Commented Feb 7, 2023 at 1:46
  • \$\begingroup\$ @DMGregory - Thanks for the comment. I added more clarification that this really isn't a design or architecture question but rather just a discussion on using the two different API models, and the pros/cons of each. \$\endgroup\$ Commented Feb 8, 2023 at 9:21
  • \$\begingroup\$ That sounds like a general programming question for StackOverflow then. Generally you want to avoid making edits to a question that change its apparent meaning after it already has answers, so you may want to revert your edits and ask your API question separately on SO, after double-checking their on-topic guidelines. (And beware of gorillas and sharks). \$\endgroup\$
    – DMGregory
    Commented Feb 8, 2023 at 12:15
  • \$\begingroup\$ I've rolled this question back to the version that was answered. If you'd like to post a different question about the pros/cons of different APIs, that will not be on-topic for this site (see the help center for details), but you can ask it elsewhere as suggested above. \$\endgroup\$
    – DMGregory
    Commented Feb 9, 2023 at 15:04

1 Answer 1

4
\$\begingroup\$

When you are separating a game engine into threads, then there are two main concerns:

  • Each worker-thread should have roughly the same amount of work to do
  • Avoid communication and data sharing between threads as much as possible, because those cause slowdowns at best and impossible to reproduce bugs at worst (race conditions and deadlocks).

For that reason it is usually not a good idea to separate threads by system. Different systems will have vastly different CPU load, and it's often hard to anticipate which ones will be the biggest resource hogs. So there is a pretty good chance that one CPU core gets to do all the work while the others idle most of the time. That's not how you make efficient use of modern multi-core CPUs. Also, threads often have a lot of shared data. Network and input are pushing events for the game mechanics and the game mechanics are pusing events for the audio, ui and network, while the rendering loop tries to visualize a world that is at the same time being changed by the game mechanics. Lots of shared data structures, which means lots of opportunities for race conditions and deadlocks.

A better approach to utilizing multiple cores is to parallelize on the task level. Look for computationally expensive tasks in your game and break them into "jobs". A "job" is an operation that runs once on a well-defined piece of data. You then distribute those jobs onto worker-threads and have certain sync-points where you wait for all running jobs to finish.

For example, let's say you have a terrain erosion mechanic in your game that is very CPU-intensive. How do you benefit from parallelization in this case?

  1. Separate the world into chunks.
  2. Create jobs to calculate the erosion for each of those chunks.
  3. Send those jobs to the worker threads
  4. Wait at a sync-point for the erosion-jobs to finish
  5. Write the changed data to the global world-state.

All of that happens each game tick.

Yes, you can run jobs in parallel that do vastly different things. This can improve performance by reducing sync-points and improving core utilization. But when you do that, you need to make sure that those parallel tasks don't share any data that gets changed at the same time.


Nevertheless, there are some systems that aren't really CPU-intense but still require their own threads due to real-time requirements. Those are often audio, input and networking. Although there are middlewares and libraries which can abstract those away. If you can use those, you should. But if you want to do them yourself (you are creating an own game engine in 2023, so you got to be really committed to the do-it-yourself mindset), then the usual way to communicate between those threads and the main game loop are event queues. When a network or input event occurs, that event gets pushed to a queue. At a controlled point of the main game loop, the main thread empties that queue. That queue needs to be thread-safe to avoid data corruption in case that an event gets pushed while the main thread empties it.

Do not react to such events immediately when they occur, because you don't know what the main thread is doing. It is very well possible that it currently reads data that gets written by the event handler, resulting in impossible to reproduce bugs.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks! Although there is a lot of merit in what you said about designing threaded jobs, here, I am only asking about the usage of the two APIs. There are some really good architecture discussion points about just separating components into different threads or just certain types of jobs only, or designing everything into threads. But again, I am just trying to explore the topic of the pros/cons of the different API usages. \$\endgroup\$ Commented Feb 8, 2023 at 9:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .