讲述一些有关iOS中的多线程知识点,以及一些应用实例
一、多线程基础知识
多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径。
进程:正在进行中的程序被称为进程,负责程序运行的内存分配;每一个进程都有自己独立的虚拟内存空间;
线程:线程是进程中一个独立的执行路径(控制单元);一个进程中至少包含一条线程,即主线程;
任务(task)用于指代抽象的概念,表示需要执行工作。
任务分为同步任务与异步任务,它们的区别是:
同步任务优先级高,在线程中有执行顺序,不会开启新的线程。
异步任务优先级低,在线程中执行没有顺序,看cpu闲不闲。在主队列中不会开启新的线程,其他队列会开启新的线程。
队列:一种先进先出的数据结构,线程的创建和回收不需要程序员操作,由队列负责。
串行队列:队列中的任务只会顺序执行(类似跑步)
并行队列:队列中的任务通常会并发执行(类似赛跑)
全局队列:是系统开发的,直接拿过来(get)用就可以;与并行队列类似,但调试时,无法确认操作所在队列
主队列:每一个应用程序对应唯一一个主队列,直接get即可;在多线程开发中,使用主队列更新UI
iOS中的三种多线程技术
1、NSThread
(1)创建一个线程很简单
(2)需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
2、GCD--Grand Central Dispatch
(1)基于C语言的API
(2)使用block来定义任务,简洁灵活
(3)提供了更多的控制能力以及操作队列中所不能使用的底层函数
3、NSOpreation
(1)纯OC代码 操作队列,对GCD的封装.它是一个抽象类,只能继承它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
(2)是面向对象的线程技术
(3)提供了一些在gcd中不容易实现的特性,如:限制最大并发数量、操作之间的依赖关系
二、线程的使用
1、NSThread
NSThread的创建
//第一种方式
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction) object:nil];
[thread start];
//第二种方式
[NSThread detachNewThreadSelector:@selector(threadAction) toTarget:self withObject:nil];
//第三种方式 不安全 不建议使用
[self performSelectorInBackground:@selector(threadAction) withObject:nil];
NSThread的暂停
//第一种 把这段代码放进selector 线程执行的方法中
//延迟100S后执行后面的代码
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:100]];
NSThread的取消
[thread cancel];
线程之间的通信
// 在指定线程上执行操作
[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];
//在主线程中执行操作
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];
//在当前线程中执行操作
[self performSelector:@selector(run) withObject:nil];
实例 - 下载图片
- (void)downLoadImage {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil];
[thread start];
}
- (void)download {
NSLog(@"download ---- %@", [NSThread currentThread]);
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"地址"]];
UIImage *image = [UIImage imageWithData:data];
if (image) {
[self performSelectorOnMainThread:@selector(reloadImageView:) withObject:image waitUntilDone:YES];
} else {
NSLog(@"图片未下载成功");
}
}
- (void)reloadImageView:(UIImage *)image {
self.imageView.image = image;
}
GCD
GCD任务执行
//添加异步操作 无需等待
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
//添加同步操作 等待任务执行完毕
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
栅栏函数
dispatch_barrier_sync(dispatch_queue_t queue,
DISPATCH_NOESCAPE dispatch_block_t block);
//用法 注意:栅栏函数不能使用全局并发队列 只能用自己创建的并发队列
dispatch_queue_t queue = dispatch_queue_create("zhalan", DISPATCH_QUEUE_CONCURRENT);
/*
1、封装任务
2、把任务添加到队列中
*/
dispatch_async(queue, ^{
NSLog(@"当前线程1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"当前线程2----%@",[NSThread currentThread]);
});
//栅栏函数(异步使用,同步没必要)
dispatch_barrier_sync(queue, ^{
NSLog(@"+++++++++++++栅栏+++++++++++++++++");
});
dispatch_async(queue, ^{
NSLog(@"当前线程3----%@",[NSThread currentThread]);
});
打印结果
2017-11-13 22:58:05.338419+0800 GCD[1299:75762] 当前线程2----<NSThread: 0x60000026cec0>{number = 4, name = (null)}
2017-11-13 22:58:05.338419+0800 GCD[1299:75922] 当前线程1----<NSThread: 0x60000026db00>{number = 5, name = (null)}
2017-11-13 22:58:05.338703+0800 GCD[1299:74941] +++++++++++++栅栏+++++++++++++++++
2017-11-13 22:58:05.339223+0800 GCD[1299:75922] 当前线程3----<NSThread: 0x60000026db00>{number = 5, name = (null)}
快速迭代(for循环)
//for循环是同步的 是同步线程循环的
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/**
GCD的快速迭代
第一个参数:次数
第二个参数:队列,只能传并发队列,传主队列会发生死锁,传串行队列的话没有作用
第三个参数:就像for中的i 索引
*/
//速度快 内部开子线程 由子线程并发执行 和主线程 一起完成遍历任务,任务执行时并发的 无顺序的
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"快速迭代: %zd %@", index ,[NSThread currentThread]);
});
队列组
//和栅栏函数比较像
//监听这一组中的其他队列 等到全部队列完成最后执行
//1、创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//2、创建队列组
dispatch_group_t group = dispatch_group_create();
//3、异步执行
/*
1、封装任务
2、把任务添加到队列中
3、监听任务的情况,通知group
*/
dispatch_group_async(group, queue, ^{
NSLog(@"队列组1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"队列组2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"队列组3");
});
dispatch_group_async(group, queue, ^{
NSLog(@"队列组4");
});
//拦截通知,当队列组中所有任务都执行完毕的时候,会进入到下面的方法
dispatch_group_notify(group, queue, ^{
NSLog(@"队列组执行完毕");
});
队列组(旧方法)
//1、创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//2、创建队列组
dispatch_group_t group = dispatch_group_create();
//3、该方法后边的异步任务中,会被纳入的队列组的监听范围中
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"队列组不同写法1");
//4、退出队列组
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"队列组不同写法2");
//4、退出队列组
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"队列组不同写法3");
//4、退出队列组
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"队列组不同写法4");
//4、退出队列组
dispatch_group_leave(group);
});
//拦截通知
//该方法是同步的还是异步的??(内部本身是异步的,不是阻塞的)
// dispatch_group_notify(group, queue, ^{
// NSLog(@"队列组执行完毕");
// });
//也可以用这个拦截通知
//第二个参数中的宏 DISPATCH_TIME_FOREVER 直到全部完成才执行
//本身是阻塞的
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"_______end _________");
使用队列组下载图片实例
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
//1、下载图片
dispatch_group_async(group,queue, ^{
//1.1确定url
NSURL *url = [NSURL URLWithString:@""];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
self.image1 = image;
NSLog(@"下载一张图片");
});
dispatch_group_async(group, queue, ^{
NSURL *url = [NSURL URLWithString:@""];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
self.image2 = image;
NSLog(@"下载第二张图片");
});
//2.合成图片
dispatch_group_notify(group, queue, ^{
//2.1 创建一张图形上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
//2.2 画图
[self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
self.image1 = nil;
[self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
self.image2 = nil;
//2.3 根据上下文得到一张图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//2.4 关闭图片上下文
UIGraphicsEndImageContext();
//3显示图片 回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 200, 200)];
imageView.image = image;
[self.view addSubview:imageView];
NSLog(@"合成图片 更新UI %@", [NSThread currentThread]);
});
});
定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"执行定时器");
});
dispatch_resume(timer);
dispatch_suspend(self.timer);//暂停
dispatch_source_cancel(self.timer);//取消
GCD中线程之间的通信
dispatch_async(dispatch_get_main_queue(), ^{
//返回主线程
});
NSOperation
NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoad1) object:nil];
[op1 start];
//未加入队列 是在主线程执行的
NSBlockOperation
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"____任务1___ %@",[NSThread currentThread]);
}];
[op1 start];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"____任务2___ %@",[NSThread currentThread]);
}];
[op2 start];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"____任务3___ %@",[NSThread currentThread]);
}];
//上面的当前线程都是主线程 下面的实在子线程中执行
//addExecutionBlock 追加方法 在子线程中添加
[op3 addExecutionBlock:^{
NSLog(@"____任务4___ %@",[NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"____任务5___ %@",[NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"____任务6___ %@",[NSThread currentThread]);
}];
[op3 start];
NSOperationQueue
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoad1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoad1) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoad1) object:nil];
/*
GCD中
1、串行队列 create和主队列
2、并发队列 create和全局并发队列glob
NSOperation中
1、主队列:[NSOperationQueue mainQueue] 和GCD中的主队列一样的 串行队列
2、非主队列:[[NSOperationQueue alloc] init] 同时具有串行和并发队列的功能(默认情况下是并发)
*/
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
//addOperation默认调用了 start方法
//上面是NSInvocationOperation 下面是使用NSBlockOperation
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"____任务1___ %@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"____任务2___ %@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"____任务3___ %@",[NSThread currentThread]);
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
NSOperation的继承用法
ZRSOperation *op1 = [[ZRSOperation alloc] init];
ZRSOperation *op2 = [[ZRSOperation alloc] init];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
//下面是ZRSOperation文件
//.h文件中
#import <Foundation/Foundation.h>
@interface ZRSOperation : NSOperation
@end
//.m文件中
#import "ZRSOperation.h"
@implementation ZRSOperation
//执行任务的时候要重写main方法 告知要执行的任务是什么
//好处 有利于代码隐蔽, 有利于代码复用
- (void)main {
[super main];
//将封装的任务 放进去
NSLog(@"main-----%@", [NSThread currentThread]);
NSLog(@"++++++++++++++++++++++=");
for (int i = 0; i < 10000; i ++) {
NSLog(@"download1---- %zd", i);
}
//解决自定义的 取消不能用
if (self.cancelled) {
return;
}
NSLog(@"++++++++++++++++++++++=");
for (int i = 0; i < 10000; i ++) {
NSLog(@"download2---- %zd", i);
}
if (self.cancelled) {
return;
}
NSLog(@"++++++++++++++++++++++=");
for (int i = 0; i < 10000; i ++) {
NSLog(@"download3---- %zd", i);
}
}
@end
添加操作依赖
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"____任务1___ %@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"____任务2___ %@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"____任务3___ %@",[NSThread currentThread]);
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//监听操作是否完成
op3.completionBlock = ^{
NSLog(@"op3已经完成了");
};
//添加依赖关系 注意不能循环依赖
[op1 addDependency:op3];
[op2 addDependency:op2];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
线程之间的通信
__block UIImage *image1;
__block UIImage *image2;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@""];
NSData *data = [NSData dataWithContentsOfURL:url];
image1 = [UIImage imageWithData:data];
//回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//刷新主线程
}];
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@""];
NSData *data = [NSData dataWithContentsOfURL:url];
image2 = [UIImage imageWithData:data];
//回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//刷新主线程
}];
}];
NSBlockOperation *complete = [NSBlockOperation blockOperationWithBlock:^{
}];
[complete addDependency:op1];
[complete addDependency:op2];
网友评论