首先,先看看进程和线程的概念。程序员
图1.1编程
这一块不难理解,重点点下他们的几个重要区别:多线程
1,地址空间和资源:进程能够申请和拥有系统资源,线程不行。资源进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。闭包
2,通讯:进程间须要用到IPC(这个能够谁总结开个课),线程能够直接读写进程的数据段来通讯(须要涉及锁,下面会简单讲到)。并发
3,调度和切换:线程快,进程慢。app
好了,切回主题,iOS多线程技术,通常有三个,NSThread,NSOperation/NSOperationQueue,GCD(Grand Central Dispatch),好,首先先抛出一些概念。异步
以上就是对于这三种技术的简单介绍,接着再来介绍下几个基本的概念,async
队列:队列是先进先出(FIFO)结构的,在iOS中,队列主要的任务是负责线程的建立、回收工做,不论什么队列和什么任务都不须要程序员参与,减轻了程序员的工做。有GCD队列和自定义队列。根据这些,咱们实际上能够操做四种队列,全局队列,主队列,串行队列,并行队列。函数
任务:分为同步任务和异步任务。同步任务:必须按照顺序执行的任务,异步任务:执行顺序不必定,哪一个先抢占到资源哪一个先执行(我试过将多个异步任务并发执行,系统会将这些任务分配给若干个线程,而后由线程调度执行,分配到同一线程的任务仍是按顺序执行的)。优化
因而乎,将这些队列和任务组合,就会出现8种组合,赶忙投票吧,分享会上用代码来详细讲解下这8种组合的效果。
下面简单介绍下以上三个技术在代码中的使用:
NSThread 使用它常常是用来查看当前线程:[NSThread currentThread](用来判断是否在主线程执行也很方便),由于其余的确实很差用,也不推荐用。
NSOperation/NSOperationQueue 虽然说是苹果基于GCD实现的面相对象的线程技术,可是感受用起来没有GCD方便,下面简单介绍下它的几个用法
NSOperation 对应任务 有如下三种用法
1 NSInvocationOperation
2 NSBlockOperation
3 自定义NSOperation 子类
1 和 2 我给出初始化方法 你们就一眼能知道区别和用法了。
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];// 调用start方法执行操做op操做
[op start];
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task0---%@", [NSThread currentThread]);
}];
[op start];
恩 其实就是调用的是block仍是方法的区别了。注意 start后是指在当前线程同步执行任务。
自定义子类也说下
主要操做就是继承NSOperation,而后在.m文件中实现- (void)mian方法,而后运行start的时候就会运行main方法里的代码。
另外 任务提供执行完成的回调
op.completeBlock能够监听一个操做执行完毕的时刻,这个block里面能够添加一些咱们须要执行的操做,这个block里面的操做仍然是在子线程执行,但不必定和被监听的操做在同一个线程
接着讲下NSOperationQueue
NSOperationQueue 对应队列 且是并行队列。
先解决个疑惑,如何使用串行队列?NSOperationQueue提供了一个设置最大并发数的方法setMaxConcurrentOperationCount: 只要将最大并发数设置为1,就是串行队列了。
首先 NSOperationQueue 能够获取到主队列 或者本身建立。
NSOperationQueue 提供 addOperation和addOperationWithBlock方法 名字取得很好,一眼就能够看出用法了,前者用来添加任务,后者用block的方式来添加任务。
同时提供cancal单个任务和cancelAllOperations全部任务的方式,不能cancel正在执行的任务。
同时提供获取挂起状态和设置挂起状态的方法。
isSuspended : 判断是否挂起
setSuspended: YES表示挂起,NO表示恢复
一样 不能对正在执行的任务设置。
任务间的执行前后,能够设置依赖来排序。
[op2 addDependency:op1];
Op1执行完成后才执行op2.
其实简单的操做用NSOperation比起GCD来要美观一下,我的感受。
最后讲重点GCD 我以为它之因此强大是由于它提供了不止前面这些功能还提供了一些经常使用的额外功能,下面我来一一讲解。
几个简单的和前面功能相似的
dispatch_get_global_queue全局队列并行队列
dispatch_get_main_queue主队列主线程中的惟一队列
dispatch_queue_create自定义队列DISPATCH_QUEUE_SERIAL串DISPATCH_QUEUE_CONCURRENT并行
dispatch_sync 同步线程
dispatch_async 异步线程
这些用法和前面相似 到时8个组合用GCD简单演示下,至关于两个一块儿理解了。
之因此说GCD比NSOperation强大,就是他不只能实现NSOperation能实现的,还有不少很好用的方法,下面一一讲解:
dispatch_once 一次性代码 使用dispatch_once保证代码只被执行一次 使用场景:单例对象的初始化 这个你们接触的不少了 很少讲了
dispatch_group_t 队列组 使用这个能够很好的控制任务调度
咱们常常有需求 须要在一个页面请求多个接口 当多个接口都完成后执行刷新UI的操做 这个时候咱们的操做通常都是多个异步返回的时候各自判断 其实用dispatch_group_t 能够很好的解决这个问题
思路以下
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 异步取数据A
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 异步取数据B
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操做都执行完毕后,回到主线程刷新UI
});
dispatch_group_notify是队列组中全部任务都完成后会发送的通知。
思考下:咱们的业务场景常常能用到这个思路,你们有没例子的?
//建立队列
self.isolationQueue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_CONCURRENT);
//改变setter
- (void)setCount:(NSUInteger)count forKey:(NSString *)key
{
key = [key copy];
//确保全部barrier都是async异步的
dispatch_barrier_async(self.isolationQueue, ^(){
if (count == 0) {
[self.counts removeObjectForKey:key];
} else {
self.counts[key] = @(count);
}
});
}
这是网上找的代码 实际上synchronized能够更好的完成这个锁操做,这是后话啦,有空再分析这个。实际上他的用法也比较多,到时搞个demo分享下。
用来替换对前一次计算没有依赖的for循环会优化不少,例如
for (int i = 0; i < 999 ; i++) {
dispatch_async(concurrentQueue, ^{
NSLog(@"wrong %d",i);
//do something hard
});
}
改用dispatch_apply 来处理
dispatch_apply(999, concurrentQueue, ^(size_t i){
NSLog(@"correct %zu",i);
//do something hard
});
Dispatch IO 文件操做
用到的方式是多个线程去读取不一样的数据块,而后再合并。
Dispatch source 这个也是程序优化的一道利器,且支持多种场景,对于程序优化颇有帮助,这块能够大力研究下,用Dispatch source来取代一些回调函数能够有效的提升程序执行的效率,codereview下咱们的代码,应该有可以用上这块的(到时奉上)。
dispatch_semaphore_wait等待信号量须要dispatch_semaphore_signal执行后才能跳过。配合使用能够解决不少复杂的线程问题。
发挥下想象力:经过这个信号量,是否是用很好的方式解决咱们请求等待同步的问题?
最后稍微讲下锁
synchronized就可解决大部分的问题了 给出ElearningConfig中的代码示例建议使用
实际上就是这段被{}包起来的代码只会被一个线程调用,而锁就是self对象。意思就是,执行代码块的时候会对self上锁,当下一个线程须要对代码块调用时,须要等待上一个程序解锁self才能够。
好啦,更多精彩内容,咱们分享会见!