Heisembugs aren't just technical debt but project killer time bombs so one must better have a perfect thread design in head that works first attempt, else is hell on earth. I can be safe in a bubble world with whole process scope individual threads or from a thread pool (so strong guarantees of joining every created thread) and having share-nothing threads communicating only by prosumer sync-queues that bring a clear information-flow picture. One can have a message pump in one thread, as GUI apps do. That is just a particular case of the prosumer channel idea before. Avoid busy waits, wait on complex event conditions by blocking calls to select() on handler-sets or WaitForMultipleObjects(). Exceptions are per thread, but is good to have a polite mechanism to make desired ones to be potentially process-fatal, and fail earliest. This won't cover all needs but is a field-tested start.