Swift Concurrency: Why are Tasks lightweight?

It is often mentioned that Task is lightweight, and that a thread is expensive. Why is this?

When the OS creates a thread it must allocate:

  • a stack — typically 512KB to 8MB of memory reserved upfront

  • kernel data structures to track the thread

  • context-switching between threads requires saving and restoring all CPU registers — the full register file, the program counter, the stack pointer

This is called preemptive context switching — the OS can interrupt a thread at any moment and swap it out. That interruption has a real cost. With many threads, the CPU spends significant time just switching between them rather than doing actual work. This is called thread explotion, which Grand Central Dispatch is actually prone to under heavy load.

A Task, on the other hand, is cheap because it owns almost nothing.

A task’s execution state — the local variables, the program counter equivalent, everything needed to resume is stored in a heap-allocated continuation object. A small chunk of memory on the heap, and not a dedicated stack.

When a task hits await, and suspends, there is no kernel involvement. The runtime just saves the continuation to the heap and moves on. When it resumes: it loads that continuation onto whatever thread is available — please note that it does not necessarily need to be the same thread the work started in.

The Swift runtime’s cooperative thread pool keeps the number of threads fixed at roughly the number of CPU cores. Those threads never block — Tasks move through them like water. Threads stay busy, the OS scheduler stays idle.

What Swift Task implement is called a green thread or fiber. A unit of concurrency entirely managed in userspace by a runtime, not the OS kernel. The kernel sees only the handful of real threads in the pool. Everything above that is the language runtime’s business.

So, when you see that Task are mentioned to be “lightweight” it is because: they have no dedicated stack, no kernel thread, no preemptive switching overhead - it just takes a heap object and a cooperative scheduler.

Next
Next

Foundation, now in Swift!