Hi, I'm Artur Olszak and this is my programming blog :-)
You can contact me here.
Some links: LinkedIn, GitHub, Stackoverflow, SoundCloud.

2017-06-10 15:12:38

Multithreading with Swift and Grand Central Dispatch

Multithreading is a holy grail for any modern application, which is trying to deliver both - background data processing and responsive feeling for a user. It's a basic mechanism which allows a single CPU to handle concurrently more than one task.

In this article we will take a closer look at one of the core APIs built in into iOS ecosystem for handling multithreading - Grand Central Dispach.

If you're starting your journey with Swift and iOS, or you just want to extend you knowledge in context of Swift, tune in for some very important informations for any professional iOS developer.

Introduction

By design any CPU can handle one process at a time during it's clock cycle. But since the processors are so fast since years, some great programmers came with an idea to switch concurrently between few different task in a manner that is invisible from user perspective.

This way you can download a large file, play some music and navigate across you app without unresponsive, frozen behaviour. Isn't that great? Yes, it is and it keeps users of your application more happy and productive at the same time.

You've heard probably about multicore processors, that CPU architecture was designed to improve processor's clock rates from the hardware perspective. In this article we will concentrate only on software solutions for mutithreading and specifically Grand Central Dispatch API built into iOS.

Terminology

In order to use GCD as a pro, you should become familiar with some basic concepts used for describing different usage scenarios and possible problems. So lets start with that from the very beginning.

Dispatch Queue

Mechanism for managing single tasks (blocks of code) in thread-safe manner. Tasks are executed in FIFO order.

Serial and Concurent

These terms are describing how single tasks should be started in context of a queue. So in serial queues all tasks are done one after another - one at a time. In concurent queues those tasks could be executed at the same time.

Synchronous and Asynchronous

It describes when a task (block of code) will return as completed to tell the queue that something was finished. Synchronous task will return after completing the code execution and asynchronous task will return immediately and will not wait for it's real completion - this way it will not block the queue for executing other tasks.

Race condition

Situation when multiple threads are operating (reading / writing) on same variable and because of that, there is an unpredictable behaviour of your code.

Thread safety

Code which can be called from mutiple threads without causing any problems is considered as thread safe. Code which is considered by it's design as not thread safety should be runned only from one thread / context - e.g. Core Data NSManagedObjectContext.

Deadlock

Situation when two (or more) concurrent tasks are waiting for each other for it's completion. Since all tasks are waiting, tasks are not started and finished.

Grand Central Dispatch API

When working with any application it's good to know what tools are available for you in order to take right decisions when designing scalable and safe architecture.

There are three predefined queue types:

  • Main queue - serial queue, which runs on the main UI thread of every application.

  • Global queues - these are concurrent queue which are shared across whole system with predefined priorities (high, default, low, background).

  • Custom queues - as name suggests, those are queues created and defined by a developer (can be serial or concurrent).

When additing new task to the queue in Swift, developer needs to specify the parameter which is deciding about the priority of that task. It's called Quality of Service, and there are four different types:

  • User-Interactive - task should be done immediately in order to provide best user experience in app UI response time.

  • User-Initiated - task is initiated from the app's UI and user is expecting immediate results for his action.

  • Utility - task is most probably long running and will take some time to finish, most of the cases trigerred by user and presenter with activity indicator or a progress bar.

  • Background - tasks which are done invisibly for a user.

Basic methods for adding tasks:

  • func sync() - adds a block object for execution on a dispatch queue and waits until that block completes.

  • func async() - adds a block object for execution without waiting for it's execution completion.

  • func asyncAfter() - adds a block object for execution with a time delay without waiting for it's execution completion.

Code snippets

Example of a global, asynchronous queue with user-initiated quality of service:

DispatchQueue.global(qos: .userInitiated).async {  

    // Run some code without blocking you UI
    // Don't update here any part of your UI !!

    DispatchQueue.main.async {  

        // Update safely your UI on main thread

    }
}

Example of main queue with delayed execution:

let delay = DispatchTime.now() + .seconds(5)  
DispatchQueue.main.asyncAfter(deadline: delay) {  

    // Execute some code after 5s delay

}

Conclusion

Mutithreading is a complex and complicated subject to study for every serious iOS dev. It's not that hard to learn and understand how to use it in context in already available library Grand Central Dispatch. Swift introduced object oriented style of GCD api and added some higher level abstraction in order to make things even easier. Now it's your turn - use it and remember that with great power comes also great responsibility.


<-- back



Name
E-mail




2017-10-23 08:12:53
Cooooll


Since 2013