{"post":{"slug":"async-vs-threads-2026","title":"Async vs Threads: Two Ways to Wait for Many Things","excerpt":"Both let a program handle many concurrent operations. One uses the operating system scheduler, the other uses a loop and cooperation, and mixing them badly is how systems hang.","category":"code","date":"January 1, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">A</span> server handling ten thousand simultaneous connections spends nearly all its time waiting — for network, for the database, for disk. The question is how to wait for ten thousand things at once without ten thousand of something expensive.\n\nTwo answers. Threads let the operating system handle it: each connection gets a thread, blocking calls block that thread, and the scheduler switches between them. Async uses one thread and an event loop: an operation that would block instead registers interest and yields, and the loop runs something else.\n\n## What changed in 2026\n\n- **Lightweight runtime threads spread.** Several languages gained threads managed by the runtime rather than the OS, which narrowed the gap considerably.\n- **Async ecosystems matured.** Blocking libraries in async ecosystems became rarer as ecosystems completed.\n- **The blocking-call problem stayed the dominant bug.** Despite better tooling, one synchronous call in an async path remained the most common failure.\n- **Structured concurrency gained ground.** Scoping concurrent tasks to a lifetime, with cancellation propagation, improved reliability in both models.\n\n## The comparison\n\n| | Threads | Async |\n| --- | --- | --- |\n| Scheduling | Operating system, preemptive | Event loop, cooperative |\n| Cost per concurrent task | Memory for a stack | Small object |\n| Practical concurrency limit | Thousands | Hundreds of thousands |\n| Blocking calls | Block one thread | Block everything |\n| CPU-bound work | Uses multiple cores | Blocks the loop |\n| Debugging | Familiar stack traces | Harder to follow |\n| Data races | Possible | Fewer, within one loop |\n\nThreads cost memory per thread, largely for the stack. That caps how many you can run, and OS context switching adds overhead as the count grows.\n\nAsync tasks are small objects, so hundreds of thousands are feasible. The price is that scheduling is *cooperative* — the loop can only switch when a task yields.\n\n## The blocking call problem\n\nThis is the one that matters, and it is the most common async bug in production.\n\nIn an async system, a task that performs a synchronous blocking operation — a filesystem read, a synchronous HTTP client, a CPU-heavy computation, a database driver that is not async-aware — does not yield. The event loop cannot switch. **Every other task on that loop stops**, including ones with nothing to do with the blocking work.\n\nOne synchronous call in one handler makes the whole server unresponsive for its duration. With threads, that call blocks one thread and everything else continues.\n\nThis is why async ecosystems need async-aware libraries throughout. A single synchronous dependency deep in a call chain undermines the model, and it is genuinely hard to spot — the code looks fine, and the symptom is intermittent latency across unrelated endpoints.\n\nRuntimes provide escape hatches: an explicit way to run blocking work on a separate thread pool. Using them requires knowing the call blocks, which is the difficult part.\n\n## Choosing\n\n**I/O-bound work** — waiting on network, database, or disk — suits async. Concurrency is high, per-task cost matters, and the work is waiting rather than computing.\n\n**CPU-bound work** — computation, encoding, image processing — suits threads. Async provides no benefit, because there is nothing to yield during; the work must actually run, and running it on the event loop blocks everything.\n\n**Mixed workloads** need both: async for the I/O, offloaded to a thread pool for the computation.\n\nThe lightweight-thread model that several runtimes now provide is worth noting because it largely dissolves the dilemma: you write straightforward blocking code, and the runtime yields the underlying OS thread when you block. That gets async-like scalability with threaded-style code, and where it is available it is frequently the right default.\n\n## Common mistakes\n\n- **A blocking call in an async handler.** Stalls everything.\n- **Async for CPU-bound work.** Complexity, no benefit.\n- **Unbounded task spawning.** Cheap tasks are not free; thousands still consume memory and scheduler time.\n- **Thread pools sized to concurrency rather than cores.** For CPU-bound work, more threads than cores adds switching overhead — see [thread pools](/blog/thread-pools-explained-2026).\n- **Assuming a library is async because the ecosystem is.** Check.\n- **No timeouts on async operations.** A hung operation holds its slot indefinitely.\n- **Mixing models carelessly.** Bridging between them has real pitfalls.\n\n## FAQ\n\n**Which is faster?**\nNeither, in general. Async handles more concurrent I/O-bound connections on the same hardware. Threads use multiple cores for computation. Speed depends entirely on the workload.\n\n**Do lightweight threads make async obsolete?**\nThey remove much of the motivation in runtimes that have them, and async remains dominant in ecosystems built around it. Where both are available, lightweight threads are usually simpler.\n\n**How do I find a blocking call?**\nProfiling that shows event loop stalls, or runtime instrumentation warning when a task holds the loop too long. Some runtimes detect this automatically, which is worth enabling.\n\n**What about data races?**\nThreads share memory and need synchronisation. Tasks on a single event loop do not run simultaneously, which removes many races — though state shared across loops or with thread pools still needs care.\n\n## Where to go next\n\nFor sizing thread pools, read [thread pools](/blog/thread-pools-explained-2026). For seeing where time goes in either model, [flame graphs](/blog/flame-graphs-explained-2026), and for the connection-level constraint, [connection limits](/blog/connection-limits-explained-2026).","hue":302,"metaTitle":"Async vs Threads: Two Ways to Wait for Many Things","metaDescription":"How async concurrency and threading differ, why blocking calls break event loops, and which model suits I/O-bound versus CPU-bound work.","keywords":["async vs threads","event loop blocking","io bound cpu bound","concurrency models","green threads","thread memory cost","async pitfalls","concurrency 2026"],"tldr":["**Async suits I/O-bound work; threads suit CPU-bound work** — that distinction resolves most of the choice.","**One blocking call stalls an entire async event loop** — this is the failure mode that matters.","**Threads cost memory per thread** — which caps how many you can have.","**Async cooperation is required, not enforced** — a task that never yields starves everything.","**Skip:** async for a CPU-bound workload. You get complexity and no concurrency benefit."],"readTime":"4 min read","wordCount":884},"related":[{"slug":"concurrency-explained-2026","title":"Concurrency explained in 2026: how programs do multiple things at once","excerpt":"Concurrency is about managing multiple tasks in overlapping time — not necessarily simultaneously. Getting it right in 2026 means knowing which model fits your workload.","category":"code","date":"March 10, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":314},{"slug":"synchronous-vs-asynchronous-programming-2026","title":"Synchronous vs Asynchronous Programming 2026: A Practical Guide","excerpt":"Synchronous code waits; asynchronous code lets work continue while it waits. Here is the 2026 guide to the difference, where async actually helps, where it hurts, and how concurrency and parallelism fit in.","category":"code","date":"June 30, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":260},{"slug":"what-is-an-event-loop-2026","title":"What Is an Event Loop in 2026? Plain-English Guide","excerpt":"A clear explanation of what an event loop is in 2026, how it handles many tasks without blocking, why it powers JavaScript, and where it can trip you up.","category":"code","date":"May 8, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":222}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"ai-tools-for-small-business-2026","title":"10 AI tools for small business owners that pay for themselves in 2026","excerpt":"The 10 AI tools that genuinely return more than they cost for a small business in 2026 — bookkeeping, marketing, scheduling, customer service, and more.","category":"ai","date":"January 1, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":195},"older":{"slug":"benchmark-saturation-2026","title":"Benchmark Saturation: When Everything Scores 95%","excerpt":"A benchmark stops being useful long before it reaches 100%. Once the leading models cluster within a point of each other, it has stopped telling you anything about which to choose.","category":"ai","date":"January 1, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":196}}