Concurrent programming in iOS

76
iOS平台并发编程 iOS平台并发编程介绍以及常问题分析 @blankyao 13117星期四

Transcript of Concurrent programming in iOS

Page 1: Concurrent programming in iOS

iOS平台并发编程iOS平台并发编程介绍以及常⻅见问题分析

@blankyao

13年1月17⽇日星期四

Page 2: Concurrent programming in iOS

并发(Concurrent )vs

并⾏行(Parallelism)

13年1月17⽇日星期四

Page 3: Concurrent programming in iOS

13年1月17⽇日星期四

Page 4: Concurrent programming in iOS

• Concurrency is about dealing with lots of things at once.Parallelism is about doing lots of things at once.

• Not the same, but related.

• One is about structure, one is about execution.

• Concurrency provides a way to structure a solution to solve a problem that may (but not necessarily) be parallelizable.

13年1月17⽇日星期四

Page 5: Concurrent programming in iOS

• Concurrency is about dealing with lots of things at once.Parallelism is about doing lots of things at once.

• Not the same, but related.

• One is about structure, one is about execution.

• Concurrency provides a way to structure a solution to solve a problem that may (but not necessarily) be parallelizable.

Concurrency is not Parallelism

13年1月17⽇日星期四

Page 6: Concurrent programming in iOS

iOS平台的并发编程模型

13年1月17⽇日星期四

Page 7: Concurrent programming in iOS

•原⽣生线程- NSThread

- POSIX Threads

- NSObject

• GCD

• Operation Queue

13年1月17⽇日星期四

Page 8: Concurrent programming in iOS

原⽣生线程

13年1月17⽇日星期四

Page 9: Concurrent programming in iOS

原⽣生线程NSThread

[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];

13年1月17⽇日星期四

Page 10: Concurrent programming in iOS

原⽣生线程POSIX Threadspthread_attr_t attr;pthread_t posixThreadID;int returnVal;returnVal = pthread_attr_init(&attr);assert(!returnVal);returnVal = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);assert(!returnVal);int threadError = pthread_create(&posixThreadID, &attr, &PosixThreadMainRoutine, NULL);returnVal = pthread_attr_destroy(&attr);@interface PhotoUploadTask() { FILE *cfile;}

13年1月17⽇日星期四

Page 11: Concurrent programming in iOS

原⽣生线程NSObject

[self performSelectorInBackground:@selector(saveLog:) withObject:logStr];

13年1月17⽇日星期四

Page 12: Concurrent programming in iOS

原⽣生线程

线程池

13年1月17⽇日星期四

Page 13: Concurrent programming in iOS

原⽣生线程线程池

1. 线程的创建和销毁不是free的 2. 线程的数量要控制(占资源)

3. 实现⽤用户级别的调度/控制

why?

13年1月17⽇日星期四

Page 14: Concurrent programming in iOS

原⽣生线程线程池 how?

1. 线程池管理器(ThreadPoolManager):⽤用于创建并管理线程池

2. ⼯工作线程(WorkThread):线程池中线程

3. 任务接⼝口(Task):每个任务必须实现的接⼝口,以供⼯工作线程调度任务的执⾏行

4. 任务队列(TaskQueue):⽤用于存放没有处理的任务,提供⼀一种缓冲机制

13年1月17⽇日星期四

Page 15: Concurrent programming in iOS

Task Queue

Completed Tasks

Tread Pool

13年1月17⽇日星期四

Page 16: Concurrent programming in iOS

原⽣生线程iOS的线程池

• GCD• Operation Queue

13年1月17⽇日星期四

Page 17: Concurrent programming in iOS

GCD(Grand Central Dispatch)

13年1月17⽇日星期四

Page 18: Concurrent programming in iOS

Thread1

Thread2

BlockA

Thread3

BlockB

Thread Pool Concurrent Queue

13年1月17⽇日星期四

Page 19: Concurrent programming in iOS

GCD

• dispatch_async• dispatch_queue_create• dispatch_get_global_queue• dispatch_get_main_queue

13年1月17⽇日星期四

Page 20: Concurrent programming in iOS

GCDdispatch_async

dispatch_async(dispatch_get_main_queue(), ^{ [self doSomethingOnMainThread]; });

13年1月17⽇日星期四

Page 21: Concurrent programming in iOS

GCDdispatch_get_main_queue

dispatch_async(dispatch_get_main_queue(), ^{ [self doSomethingOnMainThread]; });

13年1月17⽇日星期四

Page 22: Concurrent programming in iOS

GCDdispatch_get_global_queue

dispatch_async(dispatch_get_global_queue(0, 0), ^{ [data writeToFile: cacheFilePath atomically:YES];});

13年1月17⽇日星期四

Page 23: Concurrent programming in iOS

GCDdispatch_queue_create

dispatch_queue_create("_some_task", DISPATCH_QUEUE_CONCURRENT);

13年1月17⽇日星期四

Page 24: Concurrent programming in iOS

GCDblock的内存问题

@property (nonatomic, copy) void(^successBlock)(void)

13年1月17⽇日星期四

Page 25: Concurrent programming in iOS

GCDblock的内存问题

self.block = ^{ [self doSomething];};

13年1月17⽇日星期四

Page 26: Concurrent programming in iOS

GCDblock的内存问题

self.block = ^{ [self doSomething];};

循环引⽤用

13年1月17⽇日星期四

Page 27: Concurrent programming in iOS

GCDblock的内存问题

self.block = ^{ [self doSomething];};

循环引⽤用 !!!

13年1月17⽇日星期四

Page 28: Concurrent programming in iOS

GCDblock的内存问题

__block typeof(self) bself = self;self.block = ^{ [bself doSomething];};

13年1月17⽇日星期四

Page 29: Concurrent programming in iOS

GCDblock的内存问题

__block typeof(self) bself = self;self.block = ^{ [bself doSomething];};

- (void)dealloc { self.block = nil; [super dealloc];}

13年1月17⽇日星期四

Page 30: Concurrent programming in iOS

GCDblock的内存问题

__weak typeof(self) bself = self;self.block = ^{ [bself doSomething];};

⾮非ARC环境替代⽅方案 https://github.com/mikeash/MAZeroingWeakRef

13年1月17⽇日星期四

Page 31: Concurrent programming in iOS

GCDblock的内存问题

__weak typeof(self) bself = self;self.block = ^{ [bself doSomething];};

ARC only

⾮非ARC环境替代⽅方案 https://github.com/mikeash/MAZeroingWeakRef

13年1月17⽇日星期四

Page 32: Concurrent programming in iOS

Operation Queue

13年1月17⽇日星期四

Page 33: Concurrent programming in iOS

Operation Queue

[[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self doSomethingOnMainThread];}];

13年1月17⽇日星期四

Page 34: Concurrent programming in iOS

CLHttpRequest.h

@interface CLHttpRequest : NSOperation@end

CLRequestManager.m

- (void)addHttpRequest:(CLHttpRequest *)aRequest{ [[NSOperationQueue mainQueue] addOperation:aRequest];}

Operation Queue

13年1月17⽇日星期四

Page 35: Concurrent programming in iOS

NSOperation

Operation Queue

MyOperation.h@interface MyOperation : NSOperation@end

MyOperation.m@implementation MyOperation- (void)main { //do anything you want}@end

13年1月17⽇日星期四

Page 36: Concurrent programming in iOS

NSOperation

Operation Queue

NSInvocationOperationNSBlockOperation

MyOperation.h@interface MyOperation : NSOperation@end

MyOperation.m@implementation MyOperation- (void)main { //do anything you want}@end

13年1月17⽇日星期四

Page 37: Concurrent programming in iOS

好处

Operation Queue

• OOP(官⽅方推荐)• 任务对象接⼝口⽐比较多(NSOperation)• 可以设置依赖• 可以设置最⼤大并发数

13年1月17⽇日星期四

Page 38: Concurrent programming in iOS

好处

Operation Queue

• OOP(官⽅方推荐)• 任务对象接⼝口⽐比较多(NSOperation)• 可以设置依赖• 可以设置最⼤大并发数

@interface NSOperationQueue : NSObject {- (void)addOperation:(NSOperation *)op;- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait - (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);- (NSArray *)operations;- (NSUInteger)operationCount NS_AVAILABLE(10_6, 4_0);- (NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;- (void)setSuspended:(BOOL)b;- (BOOL)isSuspended;- (void)setName:(NSString *)n NS_AVAILABLE(10_6, 4_0);- (NSString *)name NS_AVAILABLE(10_6, 4_0);- (void)cancelAllOperations;- (void)waitUntilAllOperationsAreFinished;+ (id)currentQueue NS_AVAILABLE(10_6, 4_0);+ (id)mainQueue NS_AVAILABLE(10_6, 4_0);@end

13年1月17⽇日星期四

Page 39: Concurrent programming in iOS

好处

Operation Queue

• OOP(官⽅方推荐)• 任务对象接⼝口⽐比较多(NSOperation)• 可以设置依赖• 可以设置最⼤大并发数

13年1月17⽇日星期四

Page 40: Concurrent programming in iOS

同步

13年1月17⽇日星期四

Page 41: Concurrent programming in iOS

shared memory

13年1月17⽇日星期四

Page 42: Concurrent programming in iOS

引⽤用计数已经⼤大⼤大减⼩小了同步的难度

13年1月17⽇日星期四

Page 43: Concurrent programming in iOS

13年1月17⽇日星期四

Page 44: Concurrent programming in iOS

互斥可⻅见

13年1月17⽇日星期四

Page 45: Concurrent programming in iOS

@synchronized

@synchronized(anObj){ // Everything between the braces is protected by the @synchronized directive.}

13年1月17⽇日星期四

Page 46: Concurrent programming in iOS

MutexBOOL moreToDo = YES;NSLock *theLock = [[NSLock alloc] init];...while (moreToDo) { /* Do another increment of calculation */ /* until there’s no more to do. */ if ([theLock tryLock]) { /* Update display used by all threads. */ [theLock unlock]; }}

13年1月17⽇日星期四

Page 47: Concurrent programming in iOS

Recursive lockNSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];void MyRecursiveFunction(int value){ [theLock lock]; if (value != 0) { --value; MyRecursiveFunction(value); } [theLock unlock];}MyRecursiveFunction(5);

13年1月17⽇日星期四

Page 48: Concurrent programming in iOS

NSConditionLockproducer.m thread:id condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA];while(YES){ [condLock lock]; /* Add data to the queue. */ [condLock unlockWithCondition:HAS_DATA];}

consumer thread:while (YES){ [condLock lockWhenCondition:HAS_DATA]; /* Remove data from the queue. */ [condLock unlockWithCondition:(isEmpty ? NO_DATA : HAS_DATA)]; // Process the data locally.}

13年1月17⽇日星期四

Page 49: Concurrent programming in iOS

Read-write lock- (id)objectAtIndex:(NSUInteger)index { __block id obj; dispatch_sync(self.concurrent_queue, ^{ obj = [self.arrayobjectAtIndex:index]}); return obj;}

- (void)insertObject:(id)obj atIndex:(NSUInteger)index { dispatch_barrier_async(self.concurrent_queue, ^{ [self.array insertObject:obj atIndex:index]; });}@end

13年1月17⽇日星期四

Page 50: Concurrent programming in iOS

⼀一切问题都搞定了吗?

13年1月17⽇日星期四

Page 51: Concurrent programming in iOS

• 不配对

• 太少

• 太多

• 顺序不对

• 死锁

锁带来的问题

13年1月17⽇日星期四

Page 52: Concurrent programming in iOS

原⼦子操作

13年1月17⽇日星期四

Page 53: Concurrent programming in iOS

OSAtomicAdd32OSAtomicAdd32BarrierOSAtomicAdd64OSAtomicAdd64BarrierOSAtomicIncrement32OSAtomicIncrement32BarrierOSAtomicIncrement64OSAtomicIncrement64BarrierOSAtomic...

13年1月17⽇日星期四

Page 54: Concurrent programming in iOS

⽆无锁

13年1月17⽇日星期四

Page 55: Concurrent programming in iOS

⽆无锁

⽆无阻塞型同步

13年1月17⽇日星期四

Page 56: Concurrent programming in iOS

CAS

int compare_and_swap (int* reg, int oldval, int newval){ int old_reg_val = *reg; if (old_reg_val == oldval) *reg = newval; return old_reg_val;}

13年1月17⽇日星期四

Page 57: Concurrent programming in iOS

• Spin Lock

• Seqlock

• Read-Copy-Update

http://code.oa.com/v2/weima/share

13年1月17⽇日星期四

Page 58: Concurrent programming in iOS

13年1月17⽇日星期四

Page 59: Concurrent programming in iOS

• 减少争⽤用

• 使⽤用读写锁

• 减少锁(使⽤用原⼦子操作或⽀支持并发的数据结构)

• 减⼩小锁的粒度

13年1月17⽇日星期四

Page 60: Concurrent programming in iOS

要注意的问题

13年1月17⽇日星期四

Page 61: Concurrent programming in iOS

不要让主线程太累

13年1月17⽇日星期四

Page 62: Concurrent programming in iOS

不要让主线程太累

autorelease pool(⾮非主线程要⾃自⼰己搞,GCD除外)

13年1月17⽇日星期四

Page 63: Concurrent programming in iOS

不要让主线程太累autorelease pool(⾮非主线程要⾃自⼰己搞,GCD除外)

正确的使⽤用volatile

13年1月17⽇日星期四

Page 64: Concurrent programming in iOS

不要让主线程太累autorelease pool(⾮非主线程要⾃自⼰己搞,GCD除外)正确的使⽤用volatile

效率问题( T = S + P/N)

13年1月17⽇日星期四

Page 65: Concurrent programming in iOS

不要让主线程太累autorelease pool(⾮非主线程要⾃自⼰己搞,GCD除外)正确的使⽤用volatile

⽣生命周期效率问题( T = S + P/N)

13年1月17⽇日星期四

Page 66: Concurrent programming in iOS

⽣生命周期

13年1月17⽇日星期四

Page 67: Concurrent programming in iOS

⽣生命周期

Run Loop

13年1月17⽇日星期四

Page 68: Concurrent programming in iOS

⽣生命周期Run Loop

A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming

events.

13年1月17⽇日星期四

Page 69: Concurrent programming in iOS

⽣生命周期Run Loop

A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming

events.

The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep

when there is none.

13年1月17⽇日星期四

Page 70: Concurrent programming in iOS

- (void)skeletonThreadMain{ // Set up an autorelease pool here if not using garbage collection. BOOL done = NO; // Add your sources or timers to the run loop and do any other setup. do { // Start the run loop but return after each source is handled. SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES); // If a source explicitly stopped the run loop, or if there are no // sources or timers, go ahead and exit. if((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished)) done = YES; // Check for any other exit conditions here and set the // done variable as needed. } while (!done); // Clean up code here. Be sure to release any allocated autorelease pools.}

13年1月17⽇日星期四

Page 71: Concurrent programming in iOS

Green Thread(Cocoa原⽣生不⽀支持)

13年1月17⽇日星期四

Page 72: Concurrent programming in iOS

•Native Threads (1:1)

•Green Threads (1:N)

•Hybrid (M:N)

线程模型

13年1月17⽇日星期四

Page 73: Concurrent programming in iOS

• Green Thread:co-operative• Native Thread:pre-emptive

调度⽅方式

13年1月17⽇日星期四

Page 74: Concurrent programming in iOS

✓创建开销⼩小(不是系统线程)✓调度开销⼩小(⽤用户级的调度)✓⽆无须锁来做数据同步

优点 vs 缺点

★不能充分利⽤用多核★⼀一个阻塞操作会阻塞所有的

13年1月17⽇日星期四

Page 75: Concurrent programming in iOS

参考资料• http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Multithreading/

Introduction/Introduction.html

• https://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html

• http://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/

• http://www.ibm.com/developerworks/cn/java/j-concurrent/

• http://www.ibm.com/developerworks/cn/java/j-jtp04186/

• http://msdn.microsoft.com/en-us/magazine/cc163744.aspx

• http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide

• http://www.ibm.com/developerworks/cn/java/j-lo-concurrent/index.html

• http://www.ibm.com/developerworks/cn/linux/l-cn-lockfree/index.html

• http://en.wikipedia.org/wiki/Thread_%28computer_science%29

13年1月17⽇日星期四

Page 76: Concurrent programming in iOS

讨论

13年1月17⽇日星期四