Difference between dispatch_async and dispatch_sync on serial queue?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
On a serial queue, both dispatch_async and dispatch_sync submit work in order, but they differ in what the calling thread does next. dispatch_async schedules the work and returns immediately. dispatch_sync schedules the work and blocks the caller until that work finishes. That difference becomes critical on serial queues because dispatch_sync to the same queue can deadlock.
Serial Queues Run One Block at a Time
A serial queue guarantees ordered execution. If you submit three blocks, they run one after another, never at the same time.
In modern Swift, you usually write:
That queue processes tasks sequentially regardless of whether you use async or sync.
async Enqueues and Returns Immediately
With async, the block is added to the queue, and the caller continues without waiting.
The important behavior is:
- the block will run later in queue order
- the current thread does not wait for completion
This is the right choice when you want ordered work but do not need the result immediately.
sync Enqueues and Blocks the Caller
With sync, the calling thread waits until the submitted block has finished.
Here, after sync is printed only after the block completes. The queue is still serial, but now the caller is blocked during the work.
Why sync Can Deadlock on the Same Serial Queue
The classic failure case is calling sync from code that is already running on that same serial queue.
This deadlocks. The outer block is already occupying the serial queue. The inner sync call waits for the queued block to run, but the queue cannot start that inner block until the outer block finishes. Neither side can progress.
That is the most important practical difference to remember.
Ordered Execution Does Not Mean the Same Calling Semantics
On a serial queue, both methods preserve order. The difference is not ordering. The difference is whether the caller waits.
Use this mental model:
- '
async: "do this later on that queue"' - '
sync: "do this on that queue and do not let me continue until it is done"'
That distinction affects responsiveness, deadlock risk, and how you structure dependent work.
Typical Use Cases
async on a serial queue is common for:
- protecting shared mutable state without blocking the caller longer than needed
- logging or file writes that should stay ordered
- background serialization of work
sync on a serial queue is appropriate when:
- you need a value immediately
- you are calling from outside that queue
- you know blocking the caller is acceptable
For example:
This pattern uses async for mutation and sync for a safe read from callers outside the queue.
Main Queue Considerations
Using DispatchQueue.main.sync from the main thread is another deadlock pattern. The main thread is already executing the current code path, so synchronously dispatching back onto it waits forever.
That is why UI work is usually scheduled with:
not with sync from the main thread.
Common Pitfalls
The biggest mistake is calling sync on the same serial queue you are already running on. Another is assuming sync is "faster" because it starts immediately; the real issue is whether blocking is acceptable. Developers also confuse ordered execution with thread identity. A serial queue guarantees one-at-a-time work, not that the same physical thread is always used. In UI code, DispatchQueue.main.sync from the main thread is a classic deadlock trap.
Summary
- Both
asyncandsyncpreserve order on a serial queue. - '
asyncreturns immediately and does not block the caller.' - '
syncblocks the caller until the submitted work finishes.' - '
syncon the same serial queue causes deadlock.' - Use
asyncfor queued work that does not need an immediate result. - Use
synconly when you need completion before continuing and you are outside that queue.

