Whhyyyy? I don't see the need for most game engines to be executing disparate async tasks left and right, and many of the commercial ones don't.

There's usually chunky enough homogeneous processing to parallelize and get plenty of CPU utilization without having all kinds of unrelated things going on simultaneously, and especially tasks as granular as casting a magic missile spell. Besides I can't see that happening without potentially lots of thread synchronization of a kind that could defeat all the performance gains you hope to achieve.

I would suggest instead to think about data/state first and look for homogeneous loops you can apply over one type of game state that no other systems access at the time that system is running.

Then you can run that system in parallel, either parallelizing the loops or even running systems in parallel. For example, if only your movement and rendering systems access motion components, then there's a sequential order where you might run movement and then rendering repeatedly in one thread (since they share the same state) but other threads that don't deal with motion at all can be running simultaneously while that one thread is focusing on motion and rendering.

Start with the state first and seek to make it no longer shared in multiple places. State and data first, threads/tasks second. Pattern your multithreading design around the data and how it's accessed instead of a more intuitive idea of what constitutes a unit of work. Think of it more like what chunk of data can be accessed safely by a thread at a given time.

And try to find those homogeneous loops. Instead of a task for a single magic missile spell, maybe you could have a `SpellSystem` which loops through *all* spells that need processing at a given time in a homogeneous loop. Or maybe it's like a consumer where you use a concurrent queue and push spells to be processed by the spell system, at which point it wakes up when there are spells to pop off and process while the rest of your system gets on doing whatever other things it wants to do. That will lend itself much more readily to efficient parallel processing. Seek out those types of loopy control flows over data that no other system touches or accesses during a good chunk of time.

Try to associate a given type of data to a given system, like sounds for the `SoundSystem` to play (or even lower-level, what regions of memory get accessed at a given time by a given system). And of course other systems might need to directly or indirectly indicate the sounds for the sound system to play, but they can do that very cheaply at which point the `SoundSystem` does the heavy lifting work. That might seem like a subtle difference from, say, just arbitrarily throwing async sound playing tasks left and right, but it means you did kind of put that thought upfront into saying, "Okay, this type of data gets handled by this type of system in this specific part of the code and in this specific thread", and that can really help make sure you can run the application across many threads efficiently without much thread contention and, more importantly, safely.