Slide 1

Slide 1 text

Concurrency in iOS Jeff Kelley | @SlaunchaMan www.jeffkelley.org Strange Loop | September 24, 2012 Monday, September 24, 12

Slide 2

Slide 2 text

Who? Monday, September 24, 12

Slide 3

Slide 3 text

Concurrency • It isn’t enough to go fast • Moore’s Law expiring early • Expanding to multiple processor cores, not faster processors • Manually creating threaded code sucks • Different tools for different jobs Monday, September 24, 12

Slide 4

Slide 4 text

Going Fast • Processors are still getting faster, but it’s slowing down • This was predicted for 2015, but something funny happened along the way • Mobile processors have more stringent heat and power consumption needs Monday, September 24, 12

Slide 5

Slide 5 text

Going Fast • Desktop computers are going multicore • A Mac Pro can have twelve processor cores! • The fastest possible algorithm may not matter if it uses a single core Monday, September 24, 12

Slide 6

Slide 6 text

Threaded Code Some people, when confronted with a problem, think, "I know, I'll use threads," and then two they hav erpoblesms. Ned Batchelder Monday, September 24, 12

Slide 7

Slide 7 text

Threaded Code • Manually-threaded code is horrible to write • Query the number of cores • Ask them how busy they are • Create the appropriate number of threads • Do stuff on those threads, monitoring the cores to see which one to use Monday, September 24, 12

Slide 8

Slide 8 text

Yuck. Monday, September 24, 12

Slide 9

Slide 9 text

UNIX Threading Monday, September 24, 12

Slide 10

Slide 10 text

UNIX Threading • Full support for the things you already know from UNIX and BSD • pthreads, kqueues, etc. • Extremely low-level, but powerful Monday, September 24, 12

Slide 11

Slide 11 text

NSThread • Objective-C threading API • Higher-level than UNIX threads, still expose raw details • Still have to manually create/destroy threads Monday, September 24, 12

Slide 12

Slide 12 text

Threading Problems • It’s difficult to gauge current CPU use and impossible to know future use • Two programs each trying to be as multithreaded as possible will fight for resources • Lots of wasted effort and surface area for bugs • Bugs here are harder to track down and potentially extremely nasty Monday, September 24, 12

Slide 13

Slide 13 text

Thread Safety • Writing to a portion of memory on one thread while trying to read that portion of memory on another is… problematic. • All kinds of solutions for this • @synchronize(myObject) • Locks, semaphores, etc. • Core Data “thread safety” Monday, September 24, 12

Slide 14

Slide 14 text

Thread Safety • This is one problem we won’t solve today. • We will make it better. Monday, September 24, 12

Slide 15

Slide 15 text

So What’s a Developer To Do? UNIX Threading Model Grand Central Dispatch NSOperationQueue New Cocoa (Touch) APIs Monday, September 24, 12

Slide 16

Slide 16 text

So What’s a Developer To Do? • Stop managing threads on your own • Think of the things your app needs to do as units of work. • Enqueue units of work and let the OS decide how to run them • The OS has a lot more knowledge than your program does Monday, September 24, 12

Slide 17

Slide 17 text

Grand Central Dispatch Monday, September 24, 12

Slide 18

Slide 18 text

Grand Central Dispatch • C API for managing queues of work • Relies heavily on blocks, an Apple extension to the C language • Manually memory managed • Open-sourced as libdispatch • Generally pretty awesome Monday, September 24, 12

Slide 19

Slide 19 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; }); Monday, September 24, 12

Slide 20

Slide 20 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; }); Monday, September 24, 12

Slide 21

Slide 21 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; }); Monday, September 24, 12

Slide 22

Slide 22 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; }); Monday, September 24, 12

Slide 23

Slide 23 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; }); Monday, September 24, 12

Slide 24

Slide 24 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; }); Monday, September 24, 12

Slide 25

Slide 25 text

Basic Dispatch Functions • dispatch_async(queue, block); dispatch_async_f(queue, context, func); • Schedules block or function on queue, returns immediately • dispatch_sync(queue, block); dispatch_sync_f(queue, context, func); • Schedules block or function on queue, blocks until completion Monday, September 24, 12

Slide 26

Slide 26 text

Dispatch Queues • dispatch_queue_t • Main Queue • Analogous to main thread (Do your UI operations here) • dispatch_get_main_queue() Monday, September 24, 12

Slide 27

Slide 27 text

Global Queues • dispatch_get_global_queue(priority, flags); • priority is one of four constants: • DISPATCH_QUEUE_PRIORITY_BACKGROUND • DISPATCH_QUEUE_PRIORITY_LOW • DISPATCH_QUEUE_PRIORITY_NORMAL • DISPATCH_QUEUE_PRIORITY_HIGH • flags arg should always be 0 (for now) Monday, September 24, 12

Slide 28

Slide 28 text

Making Queues • dispatch_queue_create(label, attr) • Use reverse DNS for label • com.example.myQueue • attr defines the type of queue • DISPATCH_QUEUE_SERIAL • DISPATCH_QUEUE_CONCURRENT • Be sure to use dispatch_release() Monday, September 24, 12

Slide 29

Slide 29 text

Using Queues • The main queue is serial • First-in, first-out, one at a time • Global queues are concurrent • GCD automatically chooses how many (usually # of CPU cores) • You pick for queues you create Monday, September 24, 12

Slide 30

Slide 30 text

Queues To Control Access • Easy way to limit access to a piece of memory • Create a serial queue (one-at-a-time, FIFO) for the object • All access to the object goes through this queue • No lock required! Monday, September 24, 12

Slide 31

Slide 31 text

Typical GCD Pattern • dispatch_async() with a background queue to kick off work • dispatch_async() with the main queue to display the results Monday, September 24, 12

Slide 32

Slide 32 text

Demo Monday, September 24, 12

Slide 33

Slide 33 text

Grand Central Dispatch • Useful for more than just threading! • Can be used to replace the main run loop in your app • For a good, lightweight example of a C program using GCD, check out the source to Mountain Lion’s caffeinate utility • Can support timers and file notifications Monday, September 24, 12

Slide 34

Slide 34 text

Grand Central Dispatch • Manages threads for you, uses as many as it needs • Not the most user-friendly API in the world • No way to cancel a task • No way to adjust the priority of a task • Memory Management?!? Monday, September 24, 12

Slide 35

Slide 35 text

NSOperationQueue Monday, September 24, 12

Slide 36

Slide 36 text

NSOperationQueue • Much like GCD, you enqueue units of work onto queues • Unlike GCD, the units of work and the queues themselves are Objective-C objects • NSOperation and NSOperationQueue Monday, September 24, 12

Slide 37

Slide 37 text

NSOperationQueue • Operations can have priority amongst one another • [myOperation setQueuePriority:NSOperationQueuePriorityLow]; • Operations can depend on one another • [myOperation addDependency:myOtherOperation]; • Even across different queues! Monday, September 24, 12

Slide 38

Slide 38 text

NSOperationQueue • Operations are cancellable • [myOperation cancel]; • In your custom operation class, check for the canceled property Monday, September 24, 12

Slide 39

Slide 39 text

Custom Operation Class? • Two ways to create an operation • NSBlockOperation • Create an operation with a work block • Subclass NSOperation • Implement -main with your custom logic Monday, September 24, 12

Slide 40

Slide 40 text

Why Subclass • Gives you a pointer to self to call [self isCancelled] • Asynchronous operations • URL loading, geocoding, etc. • The end of main does not necessarily end the operation • Implement -start and -isFinished Monday, September 24, 12

Slide 41

Slide 41 text

Demo Monday, September 24, 12

Slide 42

Slide 42 text

NSOperationQueue • Objective-C class to manage the execution of units of work • Create custom operations to perform a unit of work • With ARC, you don’t need to worry about memory management • Can cancel and prioritize tasks Monday, September 24, 12

Slide 43

Slide 43 text

New Cocoa (Touch) APIs Monday, September 24, 12

Slide 44

Slide 44 text

New Cocoa (Touch) APIs • Sometimes you don’t want to worry about managing threads, dispatch queues, or operation queues • Common, repetitive tasks that could be made faster with concurrency, but it’s not worth the effort to create a queue and manage it • Apple wants you to write fast code Monday, September 24, 12

Slide 45

Slide 45 text

New Cocoa (Touch) APIs • Enumerating a Collection • Sorting an Array Monday, September 24, 12

Slide 46

Slide 46 text

Enumerating a Collection • A task as old as programming itself • Walk the collection, item-by-item, and do something with each one Monday, September 24, 12

Slide 47

Slide 47 text

Enumerating a Collection NSUInteger count = [myArray count]; for (int i = 0; i < count; i++) { id obj = [myArray objectAtIndex:i]; [obj doSomething]; } Monday, September 24, 12

Slide 48

Slide 48 text

Enumerating a Collection NSUInteger count = [myArray count]; for (int i = 0; i < count; i++) { id obj = [myArray objectAtIndex:i]; [obj doSomething]; for (j = 0; j < [myNewArray count]; j++) { // More code inside this loop! } } Monday, September 24, 12

Slide 49

Slide 49 text

Enumerating a Collection NSEnumerator *enum = [myArray objectEnumerator]; id object; while ((object = [enum nextObject])) { [object doSomething]; } Monday, September 24, 12

Slide 50

Slide 50 text

Enumerating a Collection NSEnumerator *enum = [myArray objectEnumerator]; id object; while ((object = [enum nextObject])) { [object doSomething]; NSUInteger i = [myArray indexOfObject:object]; } Monday, September 24, 12

Slide 51

Slide 51 text

Enumerating a Collection for (id object in myArray) { [object doSomething]; } Monday, September 24, 12

Slide 52

Slide 52 text

Enumerating a Collection size_t count = [myArray count]; dispatch_queue_t queue = ... dispatch_apply(count, queue, ^(size_t i) { id object = [myArray objectAtIndex:i]; [object doSomething]; }); Monday, September 24, 12

Slide 53

Slide 53 text

Enumerating a Collection [myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { ! [obj doSomething]; }]; Monday, September 24, 12

Slide 54

Slide 54 text

Enumerating a Collection [myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { ! [obj doSomething]; }]; Monday, September 24, 12

Slide 55

Slide 55 text

Enumerating a Collection [myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { ! [obj doSomething]; }]; Monday, September 24, 12

Slide 56

Slide 56 text

Enumerating a Collection • Concurrency for free! • Don’t worry about queue management • Very quickly add concurrency to an existing project Monday, September 24, 12

Slide 57

Slide 57 text

Sorting a Collection • NSArray and NSOrderedSet collections sometimes need sorting • Many, many algorithms • The more objects in the collection, the more time it’s going to take—potentially exponentially Monday, September 24, 12

Slide 58

Slide 58 text

Sorting a Collection NSMutableArray *myArray; [myArray sortWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(id obj1, id obj2) { ! ! ! return [obj1 compare:obj2]; ! ! }]; Monday, September 24, 12

Slide 59

Slide 59 text

Sorting a Collection NSMutableArray *myArray; [myArray sortWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(id obj1, id obj2) { ! ! ! return [obj1 compare:obj2]; ! ! }]; Monday, September 24, 12

Slide 60

Slide 60 text

Sorting a Collection NSMutableArray *myArray; [myArray sortWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(id obj1, id obj2) { ! ! ! return [obj1 compare:obj2]; ! ! }]; Monday, September 24, 12

Slide 61

Slide 61 text

Sorting a Collection NSMutableArray *myArray; [myArray sortWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(id obj1, id obj2) { ! ! ! return [obj1 compare:obj2]; ! ! }]; Monday, September 24, 12

Slide 62

Slide 62 text

Sorting a Collection • Stop worrying about sort algorithm (for most applications) • Utilize as many cores as needed to sort your data • Huge returns as hardware increases in throughput Monday, September 24, 12

Slide 63

Slide 63 text

Thread Safety • Don’t modify objects from multiple queues • Use dispatch queues to coordinate access • Use the main dispatch and operation queues for UIKit operations • Assume Apple code is not thread-safe Monday, September 24, 12

Slide 64

Slide 64 text

GCD Barriers • Great tool for thread safety • Allow for concurrent reading of data but serial writing • For instance, read from a dictionary on any queue simultaneously, write to it on a single queue Monday, September 24, 12

Slide 65

Slide 65 text

Demo Monday, September 24, 12

Slide 66

Slide 66 text

Thread Safety and Core Data • Create a separate Managed Object Context for each queue • Don’t pass NSManagedObject instances between queues • Use the object ID instead • Register for the NSManagedObjectContextDidSaveNotification notification Monday, September 24, 12

Slide 67

Slide 67 text

Wrap-Up • Concurrency is an enormous topic • Thread Safety is its own talk, especially if you use Core Data • Concurrency is not magic performance snake oil • Concurrency does help you take advantage of hardware enhancements Monday, September 24, 12

Slide 68

Slide 68 text

For More Info • http://jeffkelley.org • @SlaunchaMan • github.com/SlaunchaMan • Learn Cocoa Touch Monday, September 24, 12