多线程方式一:GCD
队列的3个种类:
- 自建队列: 分并行/串行
- 全局队列: 属于并行队列, 是系统默认创建的.
- 主队列: 主线程上的队列. 串行队列
1.自建队列四种
异步:异于当前方法所在线程 async
同步:同于当前方法所在线程 sync
串行:任务按顺序执行 SERIAL
并行:任务没有顺序,同时执行 CONCURRENT
switch (indexPath.section) {
case 0: //异步串行
{
//队列:任务是放在队列中的
//SERIAL串行,序列 CONCURRENT 并行
dispatch_queue_t queue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
//队列放方法,并且规定方法在哪个线程中执行
//把参数2的方法放到参数1的队列中,异步执行
dispatch_async(queue, ^{
[self doMethod0];
});
dispatch_async(queue, ^{
[self doMethod1];
});
}
break;
case 1: //异步并行
{
dispatch_queue_t queue = dispatch_queue_create("并行", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[self doMethod0];
//[self doMethod1];这样写,自然就串行了
});
dispatch_async(queue, ^{
[self doMethod1];
});
}
break;
case 2://同步串行
{
[self doMethod0];
[self doMethod1];
return;
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
[self doMethod0];
});
dispatch_sync(queue, ^{
[self doMethod1];
});
}
break;
case 3: //同步并行
{
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
[self doMethod0];
});
dispatch_sync(queue, ^{
[self doMethod1];
});
}
break;
default:
break;
}
2.全局队列和主队类
- (void)viewDidLoad {
[super viewDidLoad];
//dispatch_get_global_queue获取全局队列, 参数1代表优先级, 0是默认优先级. 参数2 无用
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 10000; i++) {
NSLog(@"%@, %d", [NSThread currentThread], i);
}
//dispatch_get_main_queue() 主队列 嵌套在全局队列中 与上方for循环属于全局队列中的一个任务所以是串行的
dispatch_async(dispatch_get_main_queue(), ^{
//所有与UIKit相关的操作一定要放到主线程中
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
v.backgroundColor = [UIColor redColor];
[self.view addSubview:v];
v.center = self.view.center;
});
});
}
barrier(墙) 和分组
- (void)refreshImage{
NSArray *imagePaths = @[@"http://imgstore.cdn.sogou.com/app/a/11220002/7291_pc.jpg", @"http://b.zol-img.com.cn/desk/bizhi/image/3/960x600/1376532627798.jpg", @"http://pic27.nipic.com/20130304/11627225_191300344144_2.jpg"];
//barrier(墙): 墙必须加到自建队列中.
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[imagePaths enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSURL *url = [NSURL URLWithString:obj];
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageViews[idx].image = img;
});
});
}];
//在自建的并行队列中添加墙, 墙只有在并行队列中的所有任务都执行结束后, 才会被推倒
dispatch_barrier_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
});
return;
//可以把多个子线程放到一个分组中, 通过监听此分组的情况来决定一些操作
dispatch_group_t group = dispatch_group_create();
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[imagePaths enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSURL *url = [NSURL URLWithString:obj];
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageViews[idx].image = image;
});
});
}];
//监听分组中的线程都执行结束的时机
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@", [NSThread currentThread]);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
});
}
死锁
- (void)viewDidLoad {
[super viewDidLoad];
/*
死锁: 在`主线程` 中 调用 `dispatch_sync` 同步方法
向 `主线程` 中放入一个任务
dispatch_sync() will not return until the block has finished.
dispatch_sync()这个方法直到block完成以后,才返回.
下方的代码是 向主线程中插入一个 block. 根据官方要求 必须是这个block执行结束以后,dispatch_sync()方法才能执行完毕.
线程特点: 一个线程同时只能做一件事情, 多个任务是排队执行的.
看主线程队列: dispatch_sync()方法 -> 打印block方法
dispatch_sync()的完成需要block方法执行完毕.
因为排队问题 block 必须在 dispatch_sync() 执行完毕后再进行!!!
*/
/*
面试问题:下方方法是否有什么问题?
答: 有可能造成死锁. 因为取决于这个方法执行的线程, 只有在主线程才会死锁.. 说下死锁的三个关键点.
*/
//dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@".....");
});
//});
//如果解决这个死锁问题?
//dispatch_async()方法不要内部的block完成就可以完成
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@".....");
});
self.view.backgroundColor = [UIColor yellowColor];
}
多线程方式二:NSThread
- (void)redBtnClicked:sender{
NSLog(@"redBtnClicked %@", [NSThread currentThread]);
//在后台线程执行方法
//[self performSelectorInBackground:@selector(wasteTime) withObject:nil];
//方式2:
//[NSThread detachNewThreadSelector:@selector(wasteTime) toTarget:self withObject:nil];
//方式3:
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(wasteTime) object:nil];
thread.name = @"1606";
[thread start];//必须手动开启
}
//开启一个新的线程, 在线程中做耗时操作
- (void)wasteTime{
//[NSThread currentThread]: 获取当前方法所在的线程
//number=1 代表主线程, 其他都是子线程
for (int i = 0; i < 10000; i++) {
NSLog(@"%d , %@", i, [NSThread currentThread]);
}
//从子线程回归主线程,规避掉线程不安全的操作
[self performSelectorOnMainThread:@selector(changeUI) withObject:nil waitUntilDone:NO];
//waitUntilDone:表示是否要等待changeUI这个方法执行结束以后, 才继续执行下方代码
NSLog(@"--------------------");
}
多线程方式三:NSOperation
NSOperation 就是 使用OC语法 对 GCD做的封装, 效率上略低(可以忽略不计), 继续如何选择 看个人喜好
NSOperationQueue : 队列
NSOperation : 任务
任务
存放在 队列
中, 队列
存放在 线程
中
//NSOperation 封装了GCD之后, 额外增加的易用方法
- (void)runMethod1{
NSOperationQueue *queue = [NSOperationQueue new];
NSBlockOperation *op0 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op0 开始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op0 结束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op1 开始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op1 结束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op2 开始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op2 结束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op3 开始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op3 结束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op4 开始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op4 结束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op5 开始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op5 结束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op6 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op6 开始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op6 结束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op7 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op7 开始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op7 结束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op8 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op8 开始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op8 结束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op9 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op9 开始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op9 结束, %@", [NSThread currentThread]);
}];
//以为单线程CPU(硬件), 开启多线程, 是伪多线程. 就是把cpu的线程分很多个时间片, 每个时间片做一件事. 因为速度太快了, 所以显得跟多线程一样.
//最佳的线程数量是3~5个
//NSOperatin就提供了最大并发数量的属性, 此属性必须在任务添加到队列`之前`写才有用!!
queue.maxConcurrentOperationCount = 3;
//规定线程依赖. 比如a必须等b线程做完才能进行
//op0 必须等 op7 和 op9 执行完毕 才能进行
[op0 addDependency:op7];
[op0 addDependency:op9];
//任务添加到队列中之后, 任务就会自动执行
//[queue addOperation:op0];
//waitUntilFinished:参数表示,下方的打印方法是否要等待队列中的任务执行结束之后, 再运行
[queue addOperations:@[op0, op1, op2, op3, op4, op5, op6, op7, op8, op9] waitUntilFinished:NO];
NSLog(@"!@#$##$^^&^&$^@#$");
//取消线程中的所有任务: 已经执行的任务无法取消
//[queue cancelAllOperations];
//取消个别任务
[op2 cancel]; //已经执行的取消不了
[op9 cancel]; //0依赖9, 9被取消, 0就不再依赖9
//暂停, 已经运行的任务 无法暂停
queue.suspended = YES;
sleep(10);
queue.suspended = NO;
}
//开子线程+回归主线程
- (void)runMethod0{
//[NSOperationQueue new] 自动开启子线程
[[NSOperationQueue new] addOperationWithBlock:^{
NSLog(@"[NSOperationQueue new], %@", [NSThread currentThread]);
//回归主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"[NSOperationQueue mainQueue], %@", [NSThread currentThread]);
}];
}];
}