Introduction to concurrency in iOS

Abhishek Singh
5 min readDec 31, 2021

Hi Everyone,

We are starting a new series on Concurrency in iOS. I have noticed a ton of interview questions come from concurrency and if you are the one who is looking for a change and recently has interviewed in any good product-based company, then you must have encountered one or multiple questions from concurrency.

Before we start our new series, I would like to tell you if you want to achieve some level of security in mobile applications and to get knowledge about HTTPS, Certificate pinning, JailBreak devices, and Multi-factor authentication then the below articles are for you.

So, we are going to discuss the below points in this and our coming articles. This blog may only contain their basics and their details may come in our forthcoming articles:

  1. What is concurrency?
  2. Why do we need concurrency?
  3. Disadvantages of concurrency.
  4. How we can achieve concurrency in iOS?

What is concurrency

Concurrency is the execution of multiple instruction sequences at the same time. I’ll explain it in detail but before that, we need to understand the meaning of the first line as the execution of multiple instructions means.

Let’s assume your manager has assigned you 10 tasks to complete in a day and you must notify your manager after completing every task. Once you are done with all your tasks, you simply need to notify your manager and can take leave for the day.

You start working on that task and the approaches you can take like:

  1. You pick any one task, finish it, notify manager about the finished task, and then pick the other task.
  2. You switch on multiple tasks i.e while the first task is still in progress, you skip it and move to some other task and come back to an unfinished task later.

The first option where we are only dealing with one task at a time is called sequential operation. One must have this doubt that what is wrong with the first option and why we can’t make our application sequential?

If that’s what you are also thinking, then please think harder. Let’s consider a very simple use-case where the 10th task might require very little time as compared to other task but that task still has to wait till all other task is finished.

Now, let’s get back to our concept of concurrency. The example we took is dependent only on a single person i.e “You”. This type of system works in a single thread environment where a single thread switches different tasks.

Let’s consider you are a team lead, and your role is to assign tasks to your team of size 5. Once you get this task from your manager, you can assign that task to your team member and each member can pick up a task. Based on OS, each member either work on a single task and complete it then move to another task, or can pick multiple tasks by switching between different task. This type of system is known as a Multithreaded system.

This is concurrency where multiple tasks execute at the same time.

Why do we need concurrency?

Well, we pretty much have discussed why we actually need concurrency and we also have discussed one of the use-cases where we need concurrency. Let’s see what else we can achieve by using concurrency:

  1. Better resource utilisation.
  2. Better response time.
  3. Better performance.

The above three benefits seem in line with what we discussed earlier.

Disadvantages of concurrency

While we had so many benefits of using concurrency but there are some drawbacks of using it. If not properly handled, concurrency can make a developer’s life hell. Don’t use it unless you are very comfortable with it. Some problems might come up if concurrency is not used properly and those are:

  1. DeadLocks
  2. Race condition
  3. Priority Inversion

Let’s discuss these issues in detail:-

  1. DeadLocks: To understand this task, let’s take an example where 2 tasks are running on 2 different threads. While they are executing the task, at some point in time they are both required to access the other task, and as we know they both are working parallelly, so they both wait for other tasks to complete but they end up keep on waiting. This condition is a deadlock and one simplest example for deadlock is called DispatchQueue.main.sync on the main thread. If we need to see another real example then please consider the below example:-
let lock = NSLock()class ConcurrentTask {

func taskOne(threadCall: Bool) {
threadCall ? print("This was called from first thread") : print("called from second func for first task")lock.lock()if threadCall {
sleep(1)
taskTwo(threadCall: false)
} else {
print("I am to print first task")
}
lock.unlock()
}
func taskTwo(threadCall: Bool) {threadCall ? print("This was called from second thread") : print("called from first func for second task")lock.lock()
if threadCall {
sleep(1)
taskTwo(threadCall: false)
} else {
print("I am to print first task")
}
lock.unlock()
}
}
let queue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)let obj = ConcurrentTask()queue.async {
obj.taskOne(threadCall: true)
}
queue.async {
obj.taskTwo(threadCall: true)
}

This is another example where there are 2 functions and they both are calling internally to each other. They both initiated from some other thread and as internally they both are dependent on each other so they called.

Now, for simplicity, let’s just say thread1 is processing taskOne and thread2 is processing task2. It’s a concurrent system so they started processing at the same time. While they both are working on their function and internally taskOne calls taskTwo to and taskTwo calls taskOne. So they are already processing this and meantime they tried to access other functions which cause them to be in a deadlock condition. They both will keep on waiting for the other thread to get released.

2. Race condition: This condition occurs when more than one thread accesses the same resource and tries to change its value. In such a scenario our result will not be as expected as it should be. To avoid Race conditions we can choose multiple things and one of them was using Lock which we have shown above in our deadlock example. There are more solutions to avoid Race condition, which we will discuss in our upcoming article.

3. Priority Inversion: This condition occurs when a high-priority task is needed to process a resource that was taken by a low-priority task. Now, unless low priority task is released it’s locked or finished its work, high priority task has to wait.

How we can achieve concurrency in iOS

There are three ways by which we achieve concurrency in iOS. We can list them as:

  1. Threads
  2. GCD
  3. OperationQueue

Now in GCD, we have DispatchQueue, DispatchGroup, DispatchWorkItem, DispatchSemaphore. We will learn them in detail in our upcoming articles and will see how we can avoid Deadlock, Race conditions, and Priority Inversion by using them.

This is all about concurrency and I’ll see you in our next article😊.

--

--