iOS多线程总结
1.一些概念
·进程:正在运行的可执行文件,拥有独立的虚拟内存空间和系统资源。包含一个主线程和多个子线程,若主线程退出,则该进程结束。
·线程:一个独立的、最小代码执行路径。iOS线程底层基于POSIX。
·任务:一段代码。
·串行和并行区别:允许同时执行的任务的数量,串行一次执行一个任务,而并行一次允许一次执行多个任务。
·同步和异步的区别:是否阻塞当前线程。同步阻塞,等待上一次任务的结果。异步不阻塞。
·队列和线程:串行队列一次执行一个任务,并发队列同时执行多个任务,iOS利用队列对任务进行调度,根据调度需要和系统当前负载状态创建、销毁线程,不需要手动管理线程。
在iOS中,系统与线程打交道,而我们只需定义好要调度的任务。
2.GCD
2.1
· 四个概念:
·同步 (sync):阻塞当前线程,直到block中的任务执行完毕。
·异步 (async):不阻塞当前线程,新开线程
·串行队列:遵循FIFO
·并行队列
147286-ffbb612b8d64d9a1.png
2.2创建队列
1)主队列
用来刷新UI,注意耗时操作不要放到主线程执行
dispatch_queue_t queue = dispatch_get_main_queue();
2)自定义队列 (串行,并行)
// 第二个参数传 DISPATCH_QUEUE_SERIAL 或 NULL 为串行,传DISPATCH_QUEUE_CONCURRENT为并行)
dispatch_queue_t queue = dispatch_queue_create("com.baidu.wk.testQueue", NULL);
3)全局并行队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2.3 创建任务
1)同步任务(不开线程)
dispatch_sync(, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
2)异步线程(开线程)
dispatch_async(, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
2.4 组队列
把多个队列加入一个组,整体执行完毕发出通知
dispatch_group_create
dispatch_group_async
dispatch_group_notify
//1.创建队列组
dispatch_group_t group = dispatch_group_create();
//2.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//3.多次使用队列组的方法执行任务, 只有异步方法
//3.1.执行3次循环
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-01 - %@", [NSThread currentThread]);
}
});
//3.2.主队列执行8次循环
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 8; i++) {
NSLog(@"group-02 - %@", [NSThread currentThread]);
}
});
//3.3.执行5次循环
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"group-03 - %@", [NSThread currentThread]);
}
});
//4.都完成后会自动通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
2.5 延迟操作 dispatch_after
dispatch_after(dispatch_time_t when, dispatch_queue_t queue, ^{
});
2.6 执行一次dispatch_after
一次性,用于单例设计模式,是线程安全的。性能比互斥锁高很多,apple推荐使用。
2.7 线程间通讯
在全局队列中异步执行耗时操作,之后将结果传给主线程,用于UI展示
dispatch_async(dispatch_get_global_queue(0,0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
// 更新 UI
});
});
- NSOperation
NSOperation和NSOperationQueue 分别对应GCD的任务和队列,步骤如下:
· 将要执行的任务封装到一个NSOperation 对象中。
· 将此任务添加到一个NSOperationQueue 对象中。
· NSOperation 为抽象类,有子类NSInvocationOperation 和 NSBlockOperation,只有异步没有同步。
· NSInvocationOperation:调用某selector;
NSBlockOperation: 并发执行一个或多个block - 添加任务
3.1.1 NSInvocationOperation
//1.创建NSInvocationOperation对象 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//2.开始执行 [operation start];
3.1.2 NSBlockOperation
//1.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//2.开始任务
[operation start];
addExecutionBlock: 通过这个方法可以给 Operation 添加多个并发执行的Block,会在主线程和其它的多个线程执行这些任务。类似 dispatch_group.
//1.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//2.开始任务
[operation start];
3.1.3 自定义operation (TODO)
要继承 NSOperation 类,并实现其 main() 方法,还需要实现 cancel() 在内的各种方法。
暂没用过,待补充。
3.2 将任务加入对列
· addOperation:添加一个operation 到 operation queue中
· addOperations:waitUntiFinished:添加一组operations到 operation queque 中
· addOperationWithBlock: 直接添加一个 block 到 operation queue 中,而不用创建一个 NSBlockOperation 对象。
1)
NSOperationQueue *queue = [NSOperationQueue mainQueue];
- 其他队列
/1.创建一个其他队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//3.添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//4.队列添加任务
[queue addOperation:operation];
3)设置最大并发数
思考一个问题,maxConcurrentOperationCount = 1 与 GCD 串行执行是一样的吗?
maxConcurrentOperationCount > 1就是并发, = 1是串行的效果,但 operation 的执行顺序还是一样会受其他因素影响的,比如 operation 的 isReady 状态、operation 的队列优先级等。
因此,一个串行的 operation queue 与一个串行的 dispatch queue 还是有本质区别的,因为 dispatch queue 的执行顺序一直是 FIFO 的。如果 operation 的执行顺序对我们来说非常重要,那么我们就应该在将 operation 添加到 operation queue 之前就建立好它的依赖关系。
3.3 添加依赖
-addOperations:waitUntileFinished: 这个是异步,多操作完成后后续操作。
-addDependency: 依赖关系,弥补NSOperation异步无法保证执行顺序。互相依赖会导致死锁。但可以跨队列依赖。
//1.任务一:下载图片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//2.任务二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"打水印 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//3.任务三:上传图片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"上传图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//4.设置依赖
[operation2 addDependency:operation1]; //任务二依赖任务一
[operation3 addDependency:operation2]; //任务三依赖任务二
//5.创建队列并加入任务
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
3.4 其他用法
· 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
4 线程同步
线程同步就是为了防止多个线程抢夺同一个资源造成的数据安全的问题,所采取的一种措施
· 互斥锁 :给需要同步的代码块加一个互斥锁,就可以保证每次只有一个线程访问此代码块。
@synchronized(self) {
//需要执行的代码块
}
· 同步执行:我们可以使用多线程的知识,把多个线程都要执行此段代码添加到一个串行队列,这样就实现了线程同步的概念。当然这里可以使用GCD和NSOpretion两种方案
//GCD
//需要一个全局变量queue,要让所有线程的这个操作都加到一个queue中
dispatch_sync(queue, ^{
NSInteger ticket = lastTicket;
[NSThread sleepForTimeInterval:0.1];
NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
ticket -= 1;
lastTicket = ticket;
});
//NSOperation & NSOperationQueue
//重点:1. 全局的 NSOperationQueue, 所有的操作添加到同一个queue中
// 2. 设置 queue 的 maxConcurrentOperationCount 为 1
// 3. 如果后续操作需要Block中的结果,就需要调用每个操作的waitUntilFinished,阻塞当前线程,一直等到当前操作完成,才允许执行后面的。waitUntilFinished 要在添加到队列之后!
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSInteger ticket = lastTicket;
[NSThread sleepForTimeInterval:1];
NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
ticket -= 1;
lastTicket = ticket;
}];
[queue addOperation:operation];
[operation waitUntilFinished];
. 延迟执行
[self performSelector:@selector(run:) withObject:@"abc" afterDelay:3];
dispatch_after
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO];
. 单例模式
static id _instance;
+ (instancetype)sharedTool {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[Tool alloc] init];
});
return _instance;
. GCD & NSOperation对比
NSOperation: 对 GCD 的封装,面向对象,OC,特性:operation 间依赖;
取消一个正在执行的 operation , 暂停和恢复 operation queue 等;
NSOperationQueue支持KVO,可以监测operation状态,例如,是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld);
设置优先级;
GCD:C语言,轻量级,效率更高。一次性、组队列、延时。