Oh gee I guess I'm a wizard. Lots of systems/embedded programmers roll their eyes at this kind of talk. Threads aren't really that hard. Event queues do have benefits in certain situations. They pair nicely with state machines. You can easily end up in callback hell though, and it is often difficult to integrate some long-running, atomic tasks into your event loop. You end up doing things like having a thread pool, a…
Systems/embedded programmers roll their eyes at this kind of talk because they usually control (or at least have visibility into) all of the code that goes into their stack. Threads aren't that hard under these conditions. The main problem with threads is that they're non-composable: the set of locks that a thread holds is basically an implicit dynamically-scoped global variable that can affect the correctness of the…
One technique to get a handle on this situation is making the mutexes actual explicit global variables.
"But global variables are bad" they will say. Yeah. And it reflects the reality.
"But I need a separate mutex for each object instance like they recommended in 1995 https://docs.oracle.com/javase/tutorial/essential/concurrenc... " they will say. Have fun with that.
Python and early Linux kernels use a single global mutex for access to all shared mutable state. In my experience, this is an entirely reasonable design decision for a huge majority of applications.