Threads vs async/event-loop vs processes
Multiple threads in one address space, scheduled preemptively by the OS. True parallelism on multiple cores (outside a GIL). Cheap data sharing but every shared mutation needs synchronization - this is where the bugs live. Each thread costs a stack (often ~1 MB).
One thread cooperatively multiplexes many tasks via non-blocking I/O and an event loop (Node.js, Python asyncio, Nginx). No data races within the loop because there is no preemption between awaits, so less locking. But a single blocking/CPU-bound call stalls everything, and it does not use multiple cores by itself.
Separate address spaces, no shared memory by default - communicate via IPC, pipes, or message passing. Strong fault and security isolation (one crash does not corrupt another) and no data races, at the cost of higher memory use and slower, explicit communication.
Async for high-concurrency I/O-bound work (web servers, proxies). Threads for shared-state parallelism and CPU work when sharing is cheap. Processes for isolation, fault tolerance, or to sidestep a GIL - often combined (a pool of processes each running an event loop).