multithreading on ios agenda
play

MULTITHREADING ON IOS AGENDA Multithreading Basics Interlude: - PowerPoint PPT Presentation

MULTITHREADING ON IOS AGENDA Multithreading Basics Interlude: Closures Multithreading on iOS Multithreading Challenges MULTITHREADING BASICS WHY DO WE NEED THREADS? Listen for User Input Respond to User Input Application Code Listen for User


  1. MULTITHREADING ON IOS

  2. AGENDA Multithreading Basics Interlude: Closures Multithreading on iOS Multithreading Challenges

  3. MULTITHREADING BASICS

  4. WHY DO WE NEED THREADS? Listen for User Input Respond to User Input Application Code Listen for User Input Respond to User Input Time Application Code Listen for User Input Respond to User Input Application Code …

  5. WHY DO WE NEED THREADS?

  6. MULTITHREADING BASICS @IBAction func downloadButtonPressed(sender: AnyObject) { downloadData() updateBusyIndicator() } Thread 1 (Main Thread) download updateBusy downloadData: Button Indicator: Pressed: The entire program is blocked while one piece of code is running!

  7. MULTITHREADING BASICS Long running tasks block our program. The UI freezes! Threads allow us to run multiple tasks in parallel !

  8. MULTITHREADING BASICS By using a background thread we can unblock the UI thread! Thread 1 (Main Thread) download updateBusy updateBusy updateBusy updateBusy Button Indicator: Indicator: Indicator: Indicator: Tapped: downloadData: Thread 2 (Background Thread)

  9. WHY DO WE NEED THREADS? Listen for User Input Respond to User Input Application Code Listen for User Input Respond to User Input Time Application Code Listen for User Input Respond to User Input Application Code …

  10. MULTITHREADING BASICS Main Thread Background Thread Listen for User Input Respond to User Input Application Code } Listen for User Input Respond to User Input Long-running Application Code Time Application Code task Listen for User Input Respond to User Input Application Code …

  11. MULTITHREADING BASICS Multithreading works independently of the underlying hardware architecture Multiple threads can run on a single core, each thread gets a certain amount of execution time

  12. MULTITHREADING BASICS Spawning and executing a thread and switching between threads consumes resources , especially on a single core system multithreading hurts overall performance (even though it can improve perceived performance through responsiveness ) In most UI Frameworks (including UIKit) UI updates are only possible from the main thread

  13. WHEN TO USE MULTITHREADING Improve responsiveness of application by performing long running tasks in background threads Improve Performance by utilizing multiple cores at once

  14. INTERLUDE: CLOSURES

  15. CLOSURES Anonymous functions that can capture and modify variables of their surrounding context Are reference types; can be passed around in code

  16. MULTITHREADING ON IOS

  17. MULTITHREADING ON IOS You can create a new Thread by creating an instance of NSThread (not recommended) Most of the time you should be using GCD (Grand Central Dispatch)

  18. GCD Abstraction for the concept of threads, instead we think of di ff erent queues for our application GCD determines which queues are mapped to which threads Closure based API

  19. GCD MINDSET Instead of thinking about particular threads, think whether work should happen on main or background thread Think about whether certain tasks should be performed concurrently or sequentially

  20. GCD dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { let data = downloadData() dispatch_async(dispatch_get_main_queue()) { updateUI(data) } }

  21. GCD dispatch_sync blocks the current thread until the block is executed dispatch_async let’s the current thread continue, executes the dispatched block some time in the future

  22. MULTITHREADING CHALLENGES

  23. SYNCHRONIZATION OF THREADS Parallel execution of code leads to situations where multiple threads access the same resources at (almost) the same time In many cases this is undesirable: One thread reads an instance of NSMutableArray while another thread manipulates the same instance (unpredictable result) Some operations in our applications need to be performed atomically

  24. SYNCHRONIZATION OF THREADS } -100$ Balance: 1 -100$ 1 Balance: 1000$ One atomic -900$ 2 1000$ transaction 1b -200$ 3 1b Balance: -900$ 2b 900$ 2 3b 2b Balance: Balance: -200$ -200$ 3 0$ Account Balance Update Account Transaction

  25. SYNCHRONIZATION OF THREADS Most commonly you will use a mutex lock Thread C Thread A Thread B Thread D (mutual exclusion lock) to synchronize code With a mutex lock you can lock a piece of @synchronized(self) code to only be run by one thread at a time Obj-C Code - Swift lacks this simple API: @synchronized(self) { if ( (self.balance - amount) >= 0) { self.balance -= amount; @synchronized(self) } }

  26. DEADLOCKS Deadlocks can occur when a thread is waiting for an Thread B Thread A event which cannot happen anymore These situations can occur when more than one lock is @synchronized(objectA) @synchronized(objectB) involved Example: @synchronized(objectB) @synchronized(objectA) Thread A has acquired Lock “objectA” and is trying to @synchronized(objectB) @synchronized(objectA) acquire additional Lock “objectB” Thread B has acquired Lock “objectB” and is trying to @synchronized(objectA) @synchronized(objectB) acquire additional Lock “objectA” Try to avoid acquiring multiple locks

  27. RACE CONDITIONS Race Conditions are bugs that only occur when multiple threads access shared resources in a specific order For this reason Race Conditions are very hard to debug Critical shared resources should be protected by synchronization Debugging threading issues can become almost impossible - good design upfront is extremely important

  28. SYNCHRONIZATION TOOLS USED WITH SWIFT

  29. LOCKS Swift does not (yet) have a mutex lock API like Obj-C has Developers came up with their own API: // API Definition func synced(lock: AnyObject, closure: () -> ()) { Usually using a serial 
 objc_sync_enter(lock) closure() objc_sync_exit(lock) queue is better than 
 } using locks! // Usage synced(self) { println("This is a synchronized closure") } Source: http://stackoverflow.com/questions/24045895/what-is-the-swift-equivalent-to-objective-cs-synchronized

  30. SERIAL DISPATCH QUEUES Preferred over locks - has better performance and conveys intention clearer Serial queue guarantees that all dispatched blocks get executed after each other - only one running at a time class Account { var balance = 0 let accessBankAccountQueue = dispatch_queue_create("com.makeschool.bankaccount", nil) func withdraw(amount: Int) { dispatch_sync(accessBankAccountQueue) { if ( (self.balance - amount) >= 0) { self.balance -= amount; } } } }

  31. NSOPERATIONQUEUE More abstract version of GCD Queue, based on Objective-C objects (NSOperations) instead of blocks Provides additional features: Dependencies between operations Operation Priorities Operations can be cancelled Always performs operations concurrently while respecting dependencies

  32. NSOPERATIONQUEUE let downloadQueue = NSOperationQueue() let downloadOperation = NSBlockOperation { // perform download here print("downloading") } downloadOperation.queuePriority = .Low let updateDBOperation = NSBlockOperation { // update DB here print("update DB") } // update DB after download completes updateDBOperation.addDependency(downloadOperation) // add operations to queue to get them started downloadQueue.addOperations([downloadOperation, updateDBOperation], waitUntilFinished: false)

  33. DISPATCH SEMAPHORE Dispatch semaphores are a flexible way to block one thread until another thread sends a signal Also useful to restrict access to finite resources (provide count > 0 
 in semaphore initializer) , called counting semaphore let currentOperationSemaphore = dispatch_semaphore_create(0) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { print("work") dispatch_semaphore_signal(currentOperationSemaphore) } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { dispatch_semaphore_wait(currentOperationSemaphore, DISPATCH_TIME_FOREVER) print("done") }

  34. DISPATCH GROUPS When you want to create a group of actions and wait until all actions in that group are completed you can use dispatch groups let mainQueueGroup = dispatch_group_create() for (var i = 0; i < 10; i++) { dispatch_group_async(mainQueueGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { [i] in print("Download Data: \(i)") } } dispatch_group_wait(mainQueueGroup, DISPATCH_TIME_FOREVER) print("All Downloads Completed") dispatch_group_wait blocks the current thread until all operations in the dispatch group are completed Alternatively you can define a block that shall be called when the group completes by using dispatch_group_notify

  35. DISPATCH GROUPS let mainQueueGroup = dispatch_group_create() for (var i = 0; i < 10; i++) { dispatch_group_async(mainQueueGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { [i] in print("Download Data: \(i)") } } dispatch_group_wait(mainQueueGroup, DISPATCH_TIME_FOREVER) print("All Downloads Completed") Example Output: Download Data: 2 Download Data: 0 Download Data: 3 Download Data: 1 Download Data: 4 Download Data: 5 Download Data: 6 Download Data: 7 Download Data: 8 Download Data: 9 All Downloads Completed

Recommend


More recommend