iOS Concurrency using GCD and Operations using Swift Brush Up


In this article, I am sharing some notes those I taken while I was learning concurrency in iOS application development by GCD and Operations. Concurrency is sometimes very important when there is multitasking works in an app. But doing it right requires some careful consideration.

1. What is Concurrency in Programming

Concurrency means when several tasks overlap during execution. In programming, it may occur when several tasks are assigned to different processes which are executed by different cores in a machine at the same time. Apple provides two ways to do multitasking or solving concurrency problem using Swift. One is Grand Central Dispatch and another one is Operation Queue.

2. Why we need concurrent programming solution

Suppose you have an email application. While your user using the application to read email at the same time it may check whether there is any new emails incoming in the server or not. As it is a network call, it may take some time, if these tasks work serially, your mail application halt the user interface while checking the emails in server. Concurrent programming solution can solve the issue, by checking the network things in the background thread without interrupting user and when it get new emails it may notify the user.

3. What is GCD and How it works

Grand Central Dispatch or GCD is a framework to manage concurrent operations. GCD provided and manages the FIFO Dispatch Queue where application can submit tasks in the form of block objects. The tasks submitted in the dispatch queue are executed on a pool of threads and the system will handle how to perform them.

4. What is Serial and Concurrent Queues

There are two types of dispatch queue: Serial Queue and Concurrent Queue. Tasks assigned in serial queue are executed one at a time, that means after one task completes then the next task will start.
But in concurrent queue tasks are dequeued serially and executed at the same time to run at once and can finish in any order, so there is no guarantee which task will complete first.

5. What is Synchronous and Asynchronous execution

In synchronous execution by sync method the current thread waits until the task finished before the method call returns. In asynchronous execution by async method, the method call returns immediately. Never call sync method in the main queue which will cause deadlock of the app.

6. What is Main, Global and private dispatch queue

When user runs app, the system automatically creates a serial dispatch queue which runs app’s main thread. This is called main queue, and task assigned in this queue works one at a time serially. To access in the code:

let mainQueue   = DispatchQueue.main

There are some other concurrent global dispatch queue created by the system, which can be accessed:

let globalQueue = DispatchQueue.global()

It is also possible to create a private serial or concurrent queue, which system handles automatically. To create private serial and concurrent queue:

let serialQueue = DispatchQueue(label: "net.ithinkdiff.app")
let concurQueue = DispatchQueue(label: "net.ithinkdiff.app.concurr", attributes: .concurrent)  //private concurrent queue

All codes tested in playground:

import UIKit
import PlaygroundSupport

//to run serial queue in playground always make the following true
PlaygroundPage.current.needsIndefiniteExecution = true

let mainQueue   = DispatchQueue.main
let globalQueue = DispatchQueue.global()
let serialQueue = DispatchQueue(label: "net.ithinkdiff.app")
let concurQueue = DispatchQueue(label: "net.ithinkdiff.app.concurr", attributes: .concurrent)

func workInConcurrently(_ num: Int, _ sleepTime: Int){
    print("Concurrent Work \(num)")
}

func workInSerially(num:Int){
    print("Serial Work \(num)")
    sleep(1)
}

globalQueue.async {
    workInConcurrently(1, 5)
    workInConcurrently(2, 1)
    workInConcurrently(3, 3)
    workInConcurrently(4, 1)
}

mainQueue.async {
    workInSerially(num: 1)
    workInSerially(num: 2)
    workInSerially(num: 3)
    workInSerially(num: 4)
}

7. How dispatch queue runs in playground vs app

Playground runs on the default global queue and the app UI runs on the default main queue DispatchQueue.main

8. How group of works can be done using GCD

There is a DispatchGroup class, initiating its object create a group. During assigning tasks in queue, we can also assign similar task in the same group. It is also possible to know when all the tasks in the group is complete.

import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

let workingQueue = DispatchQueue(label: "net.ithinkdiff.app", attributes: .concurrent)
let globalQueue  = DispatchQueue.global()
let defaultGroup = DispatchGroup()    //create a new group

func multiplication(_ num: (Int, Int)) -> Int{
    sleep(1) //to make the method slower
    return num.0 * num.1
}

let groupOfNumbers = [(1, 1), (5, 2), (3, 4)]

for pair in groupOfNumbers{
    //group of task assigning in working queue
    workingQueue.async(group: defaultGroup){
        let result = multiplication(pair)
        print("Result: \(result)")
    }
}

//notification
defaultGroup.notify(queue: globalQueue){
    print("Multiplication Done!")
}

9. What is Operation and how we can use it to solve multitasking

A single task can be represented by an operation. An operation can be constructed by BlockOperation, by subclassing of Operation class and assign the operation in OperationQueue. Operation is a single-shot object, after it executed once it cannot re-execute again.

//Single BlockOperation 
var result:Int?
let multiplication = BlockOperation{
    result = 5 * 10
}
multiplication.start()
result

Multiple operations using BlockOperation:

let pairOfNumbers = [(2, 3), (5, 10), (4, 5)]
let blockOperations = BlockOperation()

for pair in pairOfNumbers{
    blockOperations.addExecutionBlock {
        let result = pair.0 * pair.1
        print(result)
    }
}

blockOperations.start()

By subclassing Operation

let pairOfNumbers = [(2, 3), (5, 10), (4, 5)]

class MultiplcationOp : Operation{
    var inputPair: [(Int, Int)]?
    
    override func main() {
        guard let inputPair = inputPair else { return }
        for pair in inputPair{
            let result = pair.0 * pair.1
            print(result)
        }
    }
}

let classOp = MultiplcationOp()
classOp.inputPair = pairOfNumbers
classOp.start()

10. What is OperationQueue and How to use it

OperationQueue is responsible for scheduling and running operations in the background thread. By default OperationQueue is concurrent but if externally set maximum concurrency operation count to 1, it works like serial queue. It best suits to solve multitasking when one task is depends on other tasks completion to create a complex execution-order graphs for tasks.

import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

let pairOfNumbers  = [(2, 3), (5, 10), (4, 5)]
let operationQueue = OperationQueue()

//if set 1, it will be serial if commented it will be concurrent
operationQueue.maxConcurrentOperationCount = 1

for pair in pairOfNumbers{
    operationQueue.addOperation {
        let result = pair.0 * pair.1
        print("Result: \(result)")
        sleep(1)
    }
}

Some coding challenges which i noted and created during my learning time. All the codes here I tested in playground

1. Is this code correct

let mainQueue   = DispatchQueue.main
let globalQueue = DispatchQueue.global()

2. What is the possible output result

import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

let mainQueue   = DispatchQueue.main
let concurQueue = DispatchQueue(label: "net.ithinkdiff.app.concurr", attributes: .concurrent)

func justPrint(_ num:Int){
    print(num)
    sleep(1)
}

mainQueue.async {
    concurQueue.async {
        justPrint(1)
        justPrint(2)
        justPrint(3)
    }
    justPrint(4)
}

Possible Result
a) 1 4 2 3
b) 1 2 3 4
c) 4 1 2 3

3. What is the right result. Note: concurQueue now serial

import UIKit
import PlaygroundSupport

//to run serial queue in playground always make the following true
PlaygroundPage.current.needsIndefiniteExecution = true

let mainQueue   = DispatchQueue.main
let concurQueue = DispatchQueue(label: "net.ithinkdiff.app.concurr")

func justPrint(_ num:Int){
    print(num)
    sleep(1)
}

mainQueue.async {
    concurQueue.async {
        justPrint(1)
        justPrint(2)
        justPrint(3)
    }
    justPrint(4)
}

Possible Result:
a) 1 4 2 3
b) 1 2 3 4
c) 4 1 2 3

4. What is the possible output

import UIKit
import PlaygroundSupport

//to run serial queue in playground always make the following true
PlaygroundPage.current.needsIndefiniteExecution = true

let mainQueue   = DispatchQueue.main
let globalQueue = DispatchQueue.global()

func justPrint(_ num:Int){
    print(num)
    sleep(1)
}

globalQueue.async {
    mainQueue.async {
        justPrint(1)
        justPrint(2)
        justPrint(3)
    }
    justPrint(4)
    justPrint(5)
}

Possible Result:
a) 1 4 2 3 5
b) 4 5 1 2 3
c) 4 1 5 2 3

5. What is the possible output

import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

let workingQueue = DispatchQueue(label: "net.ithinkdiff.app")
let globalQueue  = DispatchQueue.global()
let group1       = DispatchGroup()
let group2       = DispatchGroup()

func multiplication(_ num: (Int, Int)) -> Int{
    return num.0 * num.1
}

func summation(_ num: (Int, Int)) -> Int{
    return num.0 + num.1
}

let groupOfNumbers = [(1, 1), (5, 2), (3, 4)]

for pair in groupOfNumbers{
    //group of task assigning in working queue
    workingQueue.async(group: group1){
        let result = multiplication(pair)
        print(result)
        
        let addition = summation(pair)
        print(addition)
    }
}

//notification
group1.notify(queue: globalQueue){
    print("Multiplication Done!")
}

Possible Result:
a) 1 2 10 7 12 7 Multiplication Done!
b) 1 2 10 7 7 12
c) 1 2 10 7 12 7 Summation Done!

 

ANSWERS:

1. Yes it is correct.

2. a) 1 4 2 3

3. a) 1 4 2 3

4. c) 4 1 5 2 3

5. a) 1 2 10 7 12 7 Multiplication Done!

mahmud ahsan

Computer programmer and hobbyist photographer from Bangladesh, lives in Malaysia. My [Business | Twitter | Linkedin | Instagram | Flickr | 500px]

You may also like

LEAVE A COMMENT