同步串行
// 队列引起的循环等待
// dispatch_sync(dispatch_get_main_queue(), ^{
// [self print];
// });
同步并行
// 同步并行
// dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// NSLog(@"1");
// dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// NSLog(@"2");
// });
// NSLog(@"3");
// });
//
异步串行
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1");
[self performSelector:@selector(print) withObject:nil afterDelay:2];
[[NSRunLoop currentRunLoop] run];
});
NSLog(@"3");
// 在gcd中开启的线程没有runloop,我们提交任务到runloop中是会失效的
//而performSelector:withObject:afterDelay:其实就是在内部创建了一个NSTimer,
//然后会添加到当前线程的Runloop中。所以当该方法添加到子线程中时,需要格外的注意两个地方
// 因为子线程中的runloop默认是没有启动的状态。使用run方法开启当前线程的runloop,但是一定要注意run方法和执行该延迟方法的顺序。
//而performSelector:withObject:afterDelay:其实就是在内部创建了一个NSTimer
//所以在子线程中两者的顺序必须是先执行performSelector延迟方法之后再执行run方法。
//因为run方法只是尝试想要开启当前线程中的runloop,但是如果该线程中并没有任何事件(source、timer、observer)的话,并不会成功的开启。
异步并行
NSOperation
需要和NSOperationQueue配合使用来实现多线程方案(如果直接start的话,是在主线程执行)
添加任务依赖
任务执行状态控制
最大并发量
NSOperation 实现多线程的使用步骤分为三步:
创建操作:先将需要执行的操作封装到一个 NSOperation 对象中。
创建队列:创建 NSOperationQueue 对象。
将操作加入到队列中:将 NSOperation 对象添加到 NSOperationQueue 对象中。
凡是添加到主队列中的操作,都会放到主线程中执行(注:不包括操作使用addExecutionBlock:添加的额外操作,额外操作可能在其他线程执行,感谢指正)
线程安全解决方案:可以给线程加锁,在一个线程执行该操作的时候,不允许其他线程进行操作。iOS 实现线程加锁有很多种方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/ge等等各种方式。
系统是怎么移除一个isFinished=YES的NSOperation
通过KVO的方式
常用属性方法
判断操作状态方法
- (void)cancel; 可取消操作,实质是标记 isCancelled 状态。
- (BOOL)isFinished; 判断操作是否已经结束。
- (BOOL)isCancelled; 判断操作是否已经标记为取消。
- (BOOL)isExecuting; 判断操作是否正在在运行。
- (BOOL)isReady; 判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。
操作同步
- (void)waitUntilFinished; 阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
- (void)setCompletionBlock:(void (^)(void))block; completionBlock 会在当前操作执行完毕时执行 completionBlock。
- (void)addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作 op 的完成。
- (void)removeDependency:(NSOperation *)op; 移除依赖,取消当前操作对操作 op 的依赖。
@property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组。
NSOperationQueue 常用属性和方法
取消/暂停/恢复操作
- (void)cancelAllOperations; 可以取消队列的所有操作。
- (BOOL)isSuspended; 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
- (void)setSuspended:(BOOL)b; 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。
操作同步 - (void)waitUntilAllOperationsAreFinished; 阻塞当前线程,直到队列中的操作全部执行完毕。
添加/获取操作 - (void)addOperationWithBlock:(void (^)(void))block; 向队列中添加一个 NSBlockOperation 类型操作对象。
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
- (NSArray *)operations; 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
- (NSUInteger)operationCount; 当前队列中的操作数。
获取队列
- (id)currentQueue; 获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil。
- (id)mainQueue; 获取主队列。
NSThread启动流程
start-》创建pthread-〉main函数-》执行-〉结束
iOS常见的锁
@synchronized 一般创建单例对象的时候使用
atomic
修饰属性的关键字
对被修饰对象进行院子性操作(不负责使用)
atomic作用:多线程下将属性设置为atomic可以保证读取数据的一致性。因为他将保证数据只能被一个线程占用,也就是说一个线程对属性进行写操作时,会使用自旋锁锁住该属性。不允许其他的线程对其进行读取操作了。
简而言之,atomic的作用只是给getter和setter加了个锁,atomic只能保证代码进入getter或者setter函数内部时是安全的,一旦出了getter和setter,多线程安全只能靠程序员自己保障了。所以atomic属性和使用property的多线程安全并没什么直接的联系。另外,atomic由于加锁也会带来一些性能损耗,所以我们在编写iOS代码的时候,一般声明property为nonatomic,在需要做多线程安全的场景,自己去额外加锁做同步。
atomic修饰的实际上是这个指针,也就是占8个字节内存的指针,因此就不可能随意使用多线程来操作这块内存的。因为这块内存是原子性的。是线程安全的。
真正不安全的是指针指向的那块内存区域,他是非原子性的,当多个线程去操作这块内存的时候,就会出现不安全的情况。
OSSpinLock循环等待询问,不释放资源,用于轻量级数据访问,简单的int值+ 1 -1操作
NSLock 互斥锁
//主线程中
NSLock *lock = [[NSLock alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@”线程1”);
sleep(2);
[lock unlock];
NSLog(@”线程1解锁成功”);
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//以保证让线程2的代码后执行
[lock lock];
NSLog(@”线程2”);
[lock unlock];
});
2016-08-19 14:23:09.659 ThreadLockControlDemo[1754:129663] 线程1
2016-08-19 14:23:11.663 ThreadLockControlDemo[1754:129663] 线程1解锁成功
2016-08-19 14:23:11.665 ThreadLockControlDemo[1754:129659] 线程2
NSLock 对象是在应用中用于协调多个线程操作
你不应该把这个类实现递归锁。如果在同一个线程上调用两次lock方法,将会对这个线程永久上锁。使用NSRecursiveLock类来才可以实现递归锁。
解锁一个没有被锁定的锁是一个程序错误,这个地方需要注意。
某个线程A调用lock方法。这样,NSLock将被上锁。可以执行“关键部分”,完成后,调用unlock方法。
如果,在线程A 调用unlock方法之前,另一个线程B调用了同一锁对象的lock方法。那么,线程B只有等待。直到线程A调用了unlock。
同一个线程不能连续两次调用加锁
NSRecursiveLock 递归锁
它允许同一线程多次加锁,而不会造成死锁。
发挥CPU多核(多线程)的优势
dispatch_semaphore_t 唤醒一个被动行为
GCD的常见作用
延迟执行dispatch_time
一次执行dispatch_once
dispatch_group_async的使用,调度,当所有任务执行完成之后再执行dispatch_group_notify
dispatch_barrier_async等待一些任务完成之后才能继续执行,使用barrier来等待之前任务完成,避免数据竞争等问题。
dispatch_semaphore信号量
多线程使用场景
类似聊天页的小视频和本地视频列表这样的tableView在显示视频的缩略图时,需要使用多线程来解决tableView的卡顿问题。因为对视频进行切图的操作是耗时的,如果在主线程进行,则会卡住tableView的滑动。
异步加载网络数据及解析
批量下载图片、图片压缩等处理,之后再刷新ui,
延迟执行