Upload
dongxu-yao
View
104
Download
20
Embed Size (px)
Citation preview
iOS平台并发编程iOS平台并发编程介绍以及常⻅见问题分析
@blankyao
13年1月17⽇日星期四
并发(Concurrent )vs
并⾏行(Parallelism)
13年1月17⽇日星期四
13年1月17⽇日星期四
• 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⽇日星期四
• 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⽇日星期四
iOS平台的并发编程模型
13年1月17⽇日星期四
•原⽣生线程- NSThread
- POSIX Threads
- NSObject
• GCD
• Operation Queue
13年1月17⽇日星期四
原⽣生线程
13年1月17⽇日星期四
原⽣生线程NSThread
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];
13年1月17⽇日星期四
原⽣生线程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⽇日星期四
原⽣生线程NSObject
[self performSelectorInBackground:@selector(saveLog:) withObject:logStr];
13年1月17⽇日星期四
原⽣生线程
线程池
13年1月17⽇日星期四
原⽣生线程线程池
1. 线程的创建和销毁不是free的 2. 线程的数量要控制(占资源)
3. 实现⽤用户级别的调度/控制
why?
13年1月17⽇日星期四
原⽣生线程线程池 how?
1. 线程池管理器(ThreadPoolManager):⽤用于创建并管理线程池
2. ⼯工作线程(WorkThread):线程池中线程
3. 任务接⼝口(Task):每个任务必须实现的接⼝口,以供⼯工作线程调度任务的执⾏行
4. 任务队列(TaskQueue):⽤用于存放没有处理的任务,提供⼀一种缓冲机制
13年1月17⽇日星期四
Task Queue
Completed Tasks
Tread Pool
13年1月17⽇日星期四
原⽣生线程iOS的线程池
• GCD• Operation Queue
13年1月17⽇日星期四
GCD(Grand Central Dispatch)
13年1月17⽇日星期四
Thread1
Thread2
BlockA
Thread3
BlockB
Thread Pool Concurrent Queue
13年1月17⽇日星期四
GCD
• dispatch_async• dispatch_queue_create• dispatch_get_global_queue• dispatch_get_main_queue
13年1月17⽇日星期四
GCDdispatch_async
dispatch_async(dispatch_get_main_queue(), ^{ [self doSomethingOnMainThread]; });
13年1月17⽇日星期四
GCDdispatch_get_main_queue
dispatch_async(dispatch_get_main_queue(), ^{ [self doSomethingOnMainThread]; });
13年1月17⽇日星期四
GCDdispatch_get_global_queue
dispatch_async(dispatch_get_global_queue(0, 0), ^{ [data writeToFile: cacheFilePath atomically:YES];});
13年1月17⽇日星期四
GCDdispatch_queue_create
dispatch_queue_create("_some_task", DISPATCH_QUEUE_CONCURRENT);
13年1月17⽇日星期四
GCDblock的内存问题
@property (nonatomic, copy) void(^successBlock)(void)
13年1月17⽇日星期四
GCDblock的内存问题
self.block = ^{ [self doSomething];};
13年1月17⽇日星期四
GCDblock的内存问题
self.block = ^{ [self doSomething];};
循环引⽤用
13年1月17⽇日星期四
GCDblock的内存问题
self.block = ^{ [self doSomething];};
循环引⽤用 !!!
13年1月17⽇日星期四
GCDblock的内存问题
__block typeof(self) bself = self;self.block = ^{ [bself doSomething];};
13年1月17⽇日星期四
GCDblock的内存问题
__block typeof(self) bself = self;self.block = ^{ [bself doSomething];};
- (void)dealloc { self.block = nil; [super dealloc];}
13年1月17⽇日星期四
GCDblock的内存问题
__weak typeof(self) bself = self;self.block = ^{ [bself doSomething];};
⾮非ARC环境替代⽅方案 https://github.com/mikeash/MAZeroingWeakRef
13年1月17⽇日星期四
GCDblock的内存问题
__weak typeof(self) bself = self;self.block = ^{ [bself doSomething];};
ARC only
⾮非ARC环境替代⽅方案 https://github.com/mikeash/MAZeroingWeakRef
13年1月17⽇日星期四
Operation Queue
13年1月17⽇日星期四
Operation Queue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self doSomethingOnMainThread];}];
13年1月17⽇日星期四
CLHttpRequest.h
@interface CLHttpRequest : NSOperation@end
CLRequestManager.m
- (void)addHttpRequest:(CLHttpRequest *)aRequest{ [[NSOperationQueue mainQueue] addOperation:aRequest];}
Operation Queue
13年1月17⽇日星期四
NSOperation
Operation Queue
MyOperation.h@interface MyOperation : NSOperation@end
MyOperation.m@implementation MyOperation- (void)main { //do anything you want}@end
13年1月17⽇日星期四
NSOperation
Operation Queue
NSInvocationOperationNSBlockOperation
MyOperation.h@interface MyOperation : NSOperation@end
MyOperation.m@implementation MyOperation- (void)main { //do anything you want}@end
13年1月17⽇日星期四
好处
Operation Queue
• OOP(官⽅方推荐)• 任务对象接⼝口⽐比较多(NSOperation)• 可以设置依赖• 可以设置最⼤大并发数
13年1月17⽇日星期四
好处
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⽇日星期四
好处
Operation Queue
• OOP(官⽅方推荐)• 任务对象接⼝口⽐比较多(NSOperation)• 可以设置依赖• 可以设置最⼤大并发数
13年1月17⽇日星期四
同步
13年1月17⽇日星期四
shared memory
13年1月17⽇日星期四
引⽤用计数已经⼤大⼤大减⼩小了同步的难度
13年1月17⽇日星期四
锁
13年1月17⽇日星期四
互斥可⻅见
13年1月17⽇日星期四
@synchronized
@synchronized(anObj){ // Everything between the braces is protected by the @synchronized directive.}
13年1月17⽇日星期四
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⽇日星期四
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⽇日星期四
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⽇日星期四
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⽇日星期四
⼀一切问题都搞定了吗?
13年1月17⽇日星期四
• 不配对
• 太少
• 太多
• 顺序不对
• 死锁
锁带来的问题
13年1月17⽇日星期四
原⼦子操作
13年1月17⽇日星期四
OSAtomicAdd32OSAtomicAdd32BarrierOSAtomicAdd64OSAtomicAdd64BarrierOSAtomicIncrement32OSAtomicIncrement32BarrierOSAtomicIncrement64OSAtomicIncrement64BarrierOSAtomic...
13年1月17⽇日星期四
⽆无锁
13年1月17⽇日星期四
⽆无锁
⽆无阻塞型同步
13年1月17⽇日星期四
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⽇日星期四
• Spin Lock
• Seqlock
• Read-Copy-Update
http://code.oa.com/v2/weima/share
13年1月17⽇日星期四
13年1月17⽇日星期四
• 减少争⽤用
• 使⽤用读写锁
• 减少锁(使⽤用原⼦子操作或⽀支持并发的数据结构)
• 减⼩小锁的粒度
13年1月17⽇日星期四
要注意的问题
13年1月17⽇日星期四
不要让主线程太累
13年1月17⽇日星期四
不要让主线程太累
autorelease pool(⾮非主线程要⾃自⼰己搞,GCD除外)
13年1月17⽇日星期四
不要让主线程太累autorelease pool(⾮非主线程要⾃自⼰己搞,GCD除外)
正确的使⽤用volatile
13年1月17⽇日星期四
不要让主线程太累autorelease pool(⾮非主线程要⾃自⼰己搞,GCD除外)正确的使⽤用volatile
效率问题( T = S + P/N)
13年1月17⽇日星期四
不要让主线程太累autorelease pool(⾮非主线程要⾃自⼰己搞,GCD除外)正确的使⽤用volatile
⽣生命周期效率问题( T = S + P/N)
13年1月17⽇日星期四
⽣生命周期
13年1月17⽇日星期四
⽣生命周期
Run Loop
13年1月17⽇日星期四
⽣生命周期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⽇日星期四
⽣生命周期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⽇日星期四
- (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⽇日星期四
Green Thread(Cocoa原⽣生不⽀支持)
13年1月17⽇日星期四
•Native Threads (1:1)
•Green Threads (1:N)
•Hybrid (M:N)
线程模型
13年1月17⽇日星期四
• Green Thread:co-operative• Native Thread:pre-emptive
调度⽅方式
13年1月17⽇日星期四
✓创建开销⼩小(不是系统线程)✓调度开销⼩小(⽤用户级的调度)✓⽆无须锁来做数据同步
优点 vs 缺点
★不能充分利⽤用多核★⼀一个阻塞操作会阻塞所有的
13年1月17⽇日星期四
参考资料• 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⽇日星期四
讨论
13年1月17⽇日星期四