An iPhone has a CPU, its Central Processing Unit. Technically, a CPU can only perform one operation at a time – once per clock cycle.
Multithreading allows the processor to create concurrent threads it can switch between, so multiple tasks can be executed at the same time.
It appears as if the two threads are executed at the same time, because the processor switches rapidly between executing them. As a smartphone or desktop user, you don't notice the switches because they occur so rapidly.
Performing multiple tasks at the same time is called concurrency.
Concurrency is the notion of multiple things happening at the same time. This is generally achieved either via time-slicing, or truly in parallel if multiple CPU cores are available to the host operating system.
A common example is the UI thread. Ever noticed how apps can still appear responsive and have smooth animations, even though the CPU is doing a lot of work? That's because of multithreading.
On an iPhone, the UI always has its own thread. When you're downloading a file from the web, or executing a complex task, the UI remains responsive and smooth.
This is because the iPhone switches between drawing the screen and
downloading the file quickly, until both tasks are complete. There's enough parallel CPU power available to draw the screen and download the file, without having to execute the tasks sequentially.
Then what about multicore CPUs? Such a CPU, often called a System-on-a-Chip (SoC), typically has multiple CPUs in one. This allows for truly parallel processing, instead of the concurrency by rapidly switching of multithreading.
In reality, multicore processing isn't exactly parallel. It depends greatly on the type of work the processors are executing – but that's a topic for another article.
To summarize, multithreading allows a CPU to rapidly switch between multiple tasks in such a way that it appears as if the tasks are executed simultaneously.
Don't Block The UI Thread! You can't update an app's UI outside the main thread. User Interface operations, like
showing a dialog or updating the text on a button can only be performed on the main thread. But… why?
There are a number of reasons for that, but the foremost reason
is to avoid race conditions. A race condition occurs when two tasks are executed concurrently, when they should be executed sequentially in order to be done correctly.
Conversely, some tasks should only be dispatched asynchronously to avoid a deadlock.
Drawing the UI on the screen of the iPhone is such a sequential task. The same goes for resolving Auto Layout constraints. You can't change the constraints while they're being calculated.
What if you execute a synchronous, blocking task on the main UI thread? The thread will wait until the synchronous task is executed. As a result, your apps UI may stutter, lag or become unresponsive for some time.
We can derive a few first principles from this:
- Keep the UI thread free, to keep your app responsive
- Avoid blocking the UI thread with synchronous code
The good news is that we can do both with Grand Central Dispatch! We'll move some task to the background, and then bring its result back onto the main thread to update the UI.
Executing Async Code with Grand Central Dispatch
When you need tasks executed concurrently, should you just go about creating a bunch of threads? Fortunately not! iOS has a terrific mechanism for working with concurrent tasks called
Grand Central Dispatch.
The name "Grand Central Dispatch" is a reference to Grand Central Terminal in downtown New York. Imagine the CPU as a bunch of railroads, with the traincars as tasks being executed on those lines. A dispatcher is moving the cars along the railroads to ensure that they reach their destination quickly on a limited number of lines.
Grand Central Dispatch is a wrapper around low-level code, to create threads and manage code. Its emphasis is on
dispatching, i.e. making sure that a number of tasks of variable importance and length are executed in a timeframe as reasonable as possible.