##一、基本概念ios
什么是进程: 进程是在系统运行的一个程序,每一个进程之间是独立的,每一个进程均运行在其专有且受保护的内存空间内。git
什么是线程: 一个进程想要执行任务,必须得有线程(至少一个线程),线程是进程的基本执行单位,一个进程的全部任务都必须在线程中执行。github
线程的串行: 一个线程中任务的执行是串行的,若是要在一个线程中执行多个任务,只能一个一个的按顺序执行编程
##二、多线程安全
什么是多线程:服务器
一个进程中能够开启多个线程,每一个线程能够并发/并行执行不一样的任务,多线程能够提交程序的执行效率,好比同时执行任务ABC。多线程
多线程原理:并发
同一时间,CPU只能执行一个线程,只有一个线程正在执行,多线程并发执行,实际上是CPU快速的在多个线程之间切换,若是CPU的切换线程的时间足够快,就会形成多线程并发执行的假象。框架
多线程的优缺点:异步
优势: 1.能适当的提升程序的执行效率 2.能适当的提升资源的利用率 缺点: 1.开启线程会占用必定的内存空间(主线程1M,子线程0.5M),若是开启过多的线程就会占用大量的内存空间,下降程序的性能。 2.线程越多,CPU在调度线程上的开销就越大。
##三、主线程
一个IOS程序运行之后,默认会开启一个线程,这个线程就被称为主线程或(UI线程)。主线程的主要做用是显示/刷新UI界面,处理UI事件(点击,滚动,拖拽等)。
IOS中的多线程:
iOS中有四种多线程编程的技术:
1.Pthread (基本不会使用,线程的生命周期由咱们⾃己管理)
2.NSThread(每一个Thread对象对应⼀一个线程)(使⽤用得⽐比较少,线程的⽣生命周期由咱们⾃己管理)
3.NSOperation(面向对象的线程技术)(基于gcd来实现,常常使⽤,⽣命周期由系统管理)
4.GCD(是基于C语⾔言的框架,能够充分利用多核,是苹果推荐使⽤的多线程技术)(常用,生命周期由系统管理))
以上这四种编程⽅方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。可是就目前而言,iOS的开发者,须要了解三种多线程技术的基本使⽤用过程。由于不少 框架技术分别使用了不一样多线程技术。
##四、NSThread
建立线程、执行下载任务,须要start手动开启执行
NSThread *thread_A = [[NSThread alloc] initWithTarget:self selector:@selector(run_A) object:nil]; thread_A.name = @"线程A";
启动任务
[thread_A start];
阻塞线程,等待几秒,是这个线程里面的全部任务一块儿阻塞
[NSThread sleepForTimeInterval:5];
阻塞到某个时间
[NSThread sleepUntilDate:[[NSDate date] dateByAddingTimeInterval:1000]];
方式二:指定任务直接执行不须要手动开启 start
[NSThread detachNewThreadSelector:@selector(run_A) toTarget:self withObject:nil];
取消进程
[thread_A cancel];
获取当前线程信息
+ (NSThread *)currentThread;
获取主线程信息
+ (NSThread *)mainThread;
ps:若是是有关UI更新的操做,在其它线程中处理完以后要回到主线程进行改变
##五、GCD
(GCD)是Apple开发的⼀个多核编程的解决⽅方法。该⽅方法在Mac OS X 10.6首次推出,并随后被引入到了iOS4.0中。GCD是⼀个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。
###5.1 任务和队列
在GCD中,加入了两个很是重要的概念:任务和队列。
任务:
即操做,你想要干什么,说白了就是一段代码,在 GCD 中就是一个 Block,因此添加任务十分方便。
任务有两种执行方式: 同步执行 和 异步执行,
同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕!
若是是 同步(sync) 操做,它会阻塞当前线程并等待 Block 中的任务执行完毕,而后当前线程才会继续往下运行。
若是是 异步(async)操做,当前线程会直接往下执行,它不会阻塞当前线程。
队列:
DISPATCH_QUEUE_SERIAL
和 并行队列DISPATCH_QUEUE_CONCURRENT
.放到串行队列的任务,GCD 会 FIFO(先进先出) 地取出来一个,执行一个,而后取下一个,这样一个一个的执行。
放到并行队列的任务,GCD 也会 FIFO的取出来,但不一样的是,它取出来一个就会放到别的线程,而后再取出来一个又放到另外一个的线程。这样因为取的动做很快,忽略不计,看起来,全部的任务都是一块儿执行的。不过须要注意,GCD 会根据系统资源控制并行的数量,因此若是任务不少,它并不会让全部任务同时执行。
并行队列 中的任务根据同步或异步有不一样的执行方式。请看下表:
主队列 | 全局队列 | 自定义串行队列 | 自定义并行队列 | |
---|---|---|---|---|
同步 | 死锁(一直卡主) | 在主线程中顺序执行 | 在主线程中顺序执行 | 在主线程中顺序执行 |
异步 | 在主线程中顺序执行 | 在其它线程中同时执行 | 在其它线程中顺序执行 | 在其它线程中同时执行 |
###5.2 建立队列
这是一个特殊的串行队列。它用于刷新UI,任何须要刷新UI的工做都要在主队列执行,因此通常耗时的任务都要放到别的线程执行。
同步任务:dispatch_sync,会阻塞后面的任务,必需当前任务完成后才能执行下一个
主队列中不能执行同步操做,否则会死锁
异步执行:dispatch_async,不会阻塞后面的任务,任务会立刻返回下一个任务不须要等待,当这个任务完成后会通知主线程。 dispatch_get_main_queue()
获取主队列,主队列中的任务都会在主线程中执行, 是一个串行队列
dispatch_async(dispatch_get_main_queue(), ^{ [self run_D]; });
dispatch_get_global_queue()
全局队列,是一个并行队列,能够将队列中的任务放到不一样的线程中执行
任务执行的优先级 DISPATCH_QUEUE_PRIORITY_HIGH 2 最高 DISPATCH_QUEUE_PRIORITY_DEFAULT 0 中等(默认) DISPATCH_QUEUE_PRIORITY_LOW (-2) 低 DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台执行 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self run_A]; });
若是在并行队列中同步执行任务,那么这些任务都会在主线程中按顺序执行,也就没有并发性了。
自定义的串行队列中异步执行任务,队列会把任务放到一个新的线程中按顺序执行
dispatch_queue_t serialQueue = dispatch_queue_create("串行队列", DISPATCH_QUEUE_SERIAL); dispatch_async(serialQueue, ^{ [self run_A]; });
自定义的并行队列中异步执行任务,队列会把任务放到不一样的线程中执行
ispatch_queue_t concurrentlQueue = dispatch_queue_create("并行队列", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(concurrentlQueue, ^{ [self run_A]; });
将更新UI的操做放到主队列中
dispatch_async(dispatch_get_main_queue(), ^{ self.imagView_1.image = image; });
###5.3 队列组
队列组能够将不少队列添加到一个组里,这样作的好处是,当这个组里全部的任务都执行完了,队列组会经过一个方法通知咱们。
建立一个队列组
dispatch_group_t group = dispatch_group_create();
建立队列
dispatch_queue_t queue = dispatch_queue_create("并行队列", DISPATCH_QUEUE_CONCURRENT); dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
在group中异步执行任务,block中调用局部变量须要加__block
__block UIImage *image_1; dispatch_group_async(group, queue, ^{ image_1 = [self downloadImage_one]; });
当group中的任务都执行完后,就会发送一个通知notify调用这个方法
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ self.imagView_1.image = image_1; });
###5.4 线程安全
一次执行,block中的代码只能被执行一次,是线程保护的(同时只能一个线程访问)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self run_B]; });
单例通常用这个 线程安全的单例建立
+ (instancetype)shareUser_GCD { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ user = [[self alloc] init]; }); return user; } + (instancetype)allocWithZone:(struct _NSZone *)zone { static User *user = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ user = [super allocWithZone:zone]; }); return user; }
延迟执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self run_B]; });
##六、NSOperation和NSOperationQueue
NSOperation 是苹果公司对 GCD 的封装,彻底面向对象,因此使用起来更好理解。 NSOperation 和 NSOperationQueue 分别对应 GCD 的 任务 和 队列 。操做步骤也很好理解:
1.将要执行的任务封装到一个 NSOperation 对象中。 2.将此任务添加到一个 NSOperationQueue 对象中。
直接执行会在主线程中顺序执行
NSInvocationOperation *opertaion_1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_A) object:nil];
启动任务
[opertaion_1 start];
至少会有一个任务在主线程中执行,其余任务会被放到其余线程中执行
NSBlockOperation *opertion = [NSBlockOperation blockOperationWithBlock:^{ [self run_A]; }]; [opertion addExecutionBlock:^{ [self run_B]; }]; [opertion addExecutionBlock:^{ [self run_C]; }]; [opertion start];
###6.1 NSInvocationOperation依赖关系
NSOperation 有一个很是实用的功能,那就是添加依赖。好比有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就能够用到依赖
注意:
NSInvocationOperation *opertaion_1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_A) object:nil]; NSInvocationOperation *opertaion_2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_B) object:nil]; NSInvocationOperation *opertaion_3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_C) object:nil]; // 添加依赖,opertaion_1 ————》opertaion_2 ————》opertaion_3 // [opertaion_2 addDependency:opertaion_1]; // [opertaion_3 addDependency:opertaion_2];
在这里就是opertaion_2会等opertaion_1执行完后再执行,后面以此类推
建立一个队列,默认就是并行队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
设置当前最大的执行数,能够控制是并行还串行,为1时就是串行
queue.maxConcurrentOperationCount = 1;
将任务添加到队列中任务就自动执行了
[queue addOperation:opertaion_1]; [queue addOperation:opertaion_2]; [queue addOperation:opertaion_3]; [queue addOperationWithBlock:^{ [self run_D]; }];
添加多个任务,而且能够设置等待完成
[queue addOperations:@[opertaion_1,opertaion_2,opertaion_3] waitUntilFinished:YES];
取消全部的任务
[queue cancelAllOperations];
取消指定的任务
[opertaion_1 cancel];
获取当前的队列
[NSOperationQueue currentQueue];
获取主队列
[NSOperationQueue mainQueue];
###6.2 其它方法
以上就是一些主要方法, 下面还有一些经常使用方法:
NSOperation
BOOL executing;
//判断任务是否正在执行BOOL finished;
//判断任务是否完成void (^completionBlock)(void);
//用来设置完成后须要执行的操做(void)cancel;
//取消任务(void)waitUntilFinished;
//阻塞当前线程直到此任务执行完 毕NSOperationQueue
NSUInteger operationCount;
//获取队列的任务数(void)cancelAllOperations;
//取消队列中全部的任务(void)waitUntilAllOperationsAreFinished;
//阻塞当前线程直到此队列中的全部任务执行完毕[queue setSuspended:YES];
// 暂停queue[queue setSuspended:NO];
// 继续queue##7 GCD和NSOperation的区别:
一、GCD的底层是用C来实现的,NSOperation底层从ios4开始也是⽤的GCD来实 现的;(底层实现)
二、在NSOperationQueue中,咱们能够随时取消已经设定要准备执⾏的任务(固然,已经开始的任务就⽆法阻⽌了),⽽GCD无法中止已经加入queue的block(实际上是有的,但须要许多复杂的代码);(取消任务)
三、NSOperation可以⽅便地设置依赖关系,咱们可让⼀个Operation依赖于另⼀个Operation,这样的话尽管两个Operation处于同⼀个并行队列中,但前者会直到后者执行完毕后再执⾏;(依赖关系)
四、咱们能将KVO应⽤用在NSOperation中,能够监听一个Operation是否完成或取消,这样子能⽐GCD更加有效地掌控咱们执⾏的后台任务;(监放任务的执⾏状况)
五、在NSOperation中,咱们可以设置NSOperation的priority优先级,可以使同⼀个并行队列中的任务区分前后地执行,⽽在GCD中,咱们只能区分不一样任务队列的优先级,若是要区分block任务的优先级,也须要大量的复杂代码;(优先级) 六、咱们可以对NSOperation进行继承,在这之上添加成员变量与成员方法,提⾼整个代码的复⽤度,这⽐简单地将block任务排⼊执行队列更有自由度,可以在其之上添加更多自定制的功能。(代码复用)
Demo下载: