However, sorry my lack of knowledge here, but I sincerely can't tell why.
Not a problem. Let’s see if we can clear up that confusion, eh?
First up, be aware we’re using blocks in two ways here:
As a verb, to indicate that a thread waits for some condition. This is a very common industry term.
As a noun, in a way that’s mostly synonymous with Swift’s closures. This usage is inherited from dispatch’s origins as a C-based API.
dispatchMain
parks the main thread [1]. You can think of it as blocking the main thread indefinitely [2], with infrastructure in place to wake up the main thread so that it can handle any work that is scheduled on the main queue (see below). This allows you to write a program in terms of dispatch’s asynchronous constructs [3] and keep the process running that program around in order to service that async work. Finally, when all that async work is done, you can call
exit
to force the process to terminate.
Now, back to your specific questions. You wrote:
It says there it waits for blocks to be submitted to the main queue, but how does it control which blocks are running asynchronously?
It doesn’t need to. Your code, and the code in the frameworks you call, is responsible for adding closures to queues, including the main queue. For example, this code:
let queue: DispatchQueue = …
queue.async {
print("This runs on `queue`.")
}
adds a closure (line 3) to the queue
queue
. At some point in the future dispatch will pull that closure off
queue
and execute it.
And how do we submit them to the main queue?
The main queue is just a static property of
DispatchQueue
, so you can reference it as
DispatchQueue.main
. For example, if you want to add a closure to the main queue, you could rewrite the above as:
DispatchQueue.main.async {
print("This runs on the main queue.")
}
Is that what exit(0) in your example does? Does this method, somehow, know all the threads running and it waits for all of them to finish before continuing execution?
No.
exit
just forces the process to terminate. Dispatch can’t automatically figure out when all the work inside your process is complete. Rather, if you block the process in
dispatchMain
then you are responsible for making sure it terminates. For a standard command line tool you do this explicitly by calling
exit
(and I showed in the my example). In other contexts this can be done implicitly. For example, a launchd daemon would typically be integrated with launchd’s transaction system, so the launchd knows when the daemon is ‘clean’ and can terminate it when required.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
[1] It doesn’t actually park the thread in the traditional sense, but rather it saves resources by terminating the thread while leaving your process alive. Neat-o-rama!
[2] Note that it’s declared as:
public func dispatchMain() -> Never
which means it never returns.
[3] In my example I’m using
URLSession
, which is based on dispatch internally.