iOS多线程之NSThread,GCD,NSOperation
基本概念
进程: 一个具有一定独立功能的程序关于某个数据集合的一次运行活动。可以理解成一个运行中的应用程序。
线程: 程序执行流的最小单元,线程是进程中的一个实体。
同步: 只能在当前线程按先后顺序依次执行,不开启新线程。
异步: 可以在当前线程开启多个新线程执行,可不按顺序执行。
队列: 装载线程任务的队形结构。
并发: 线程执行可以同时一起进行执行。
串行: 线程执行只能依次逐一先后有序的执行。一个进程可以有多个线程,也可以有多个队列。
iOS多线程对比
-
NSThread
每个NSThread对象对应一个线程,真正最原始的线程。
1)优点:NSThread 轻量级最低,相对简单。
2)缺点:手动管理所有的线程活动,如生命周期、线程同步、睡眠等。 -
NSOperation
自带线程管理的抽象类。
1)优点:自带线程周期管理,操作上可更注重自己逻辑。
2)缺点:面向对象的抽象类,只能实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。 -
GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。
1)优点:最高效,避开并发陷阱。
2)缺点:基于C实现。 -
选择小结
1)简单而安全的选择NSOperation实现多线程即可。
2)处理大量并发数据,又追求性能效率的选择GCD。
3)NSThread用的少。
1. NSThread 创建方法一
- (void)viewDidLoad {
[super viewDidLoad];
// 创建线程
NSThread *thread = [[NSThread alloc]initWithTarget:self selector: @selector(run:) object:@"hehe"];
//线程名字
thread.name = @"wang";
//开启线程
[thread start];
}
- (void)run:(NSString*)str
{
//d[2177:95263] -----run-----hehe--<NSThread: 0x60000026e440>{number = 3, name = wang}
NSLog(@"-----run-----%@--%@", str, [NSThread currentThread]);
}
2. NSThread 其他创建方法
//方法二
[self performSelectorInBackground:@selector(run:) withObject:@"wang"];
//方法三
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"wang"];
3. 线程的状态
9F77DB76-6699-4DFE-BAFC-C9E6A8F5B534.png4. 线程安全
//self 是锁对象
@synchronized(self) {
//执行多线程操作
}
5. NSThread的其他api
// 获得主线程
+ (NSThread *)mainThread;
// 是否为主线程
- (BOOL)isMainThread;
// 是否为主线程
+ (BOOL)isMainThread;
//获得当前线程
NSThread *current = [NSThread currentThread];
//线程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
//启动线程,进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态
- (void)start;
//阻塞(暂停)线程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//强制停止线程进入死亡状态一旦线程停止(死亡)了,就不能再次开启任务
+ (void)exit;
//线程间通信常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
6. GCD的任务和队列
- 异步函数 + 并发队列:可以同时开启多条线程
/**
* 异步函数 + 并发队列:可以同时开启多条线程
*/
- (void)asyncConcurrent
{
// 1.创建一个并发队列
// label : 相当于队列的名字
// attr : 并行 DISPATCH_QUEUE_CONCURRENT 串行 DISPATCH_QUEUE_SERIAL == NULL
// dispatch_queue_t queue = dispatch_queue_create("wang", DISPATCH_QUEUE_CONCURRENT);
// 1.获得全局的并发队列
//#define DISPATCH_QUEUE_PRIORITY_HIGH 2
//#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
//#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
//#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.将任务加入队列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<2; i++) {
NSLog(@"1-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<2; i++) {
NSLog(@"2-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<2; i++) {
NSLog(@"3-----%@", [NSThread currentThread]);
}
});
// 2017-03-28 17:10:40.609 07-GCD的基本使用[5361:272988] 3-----<NSThread: 0x60800026b9c0>{number = 5, name = (null)}
// 2017-03-28 17:10:40.609 07-GCD的基本使用[5361:272971] 1-----<NSThread: 0x60800026ba80>{number = 3, name = (null)}
// 2017-03-28 17:10:40.609 07-GCD的基本使用[5361:272973] 2-----<NSThread: 0x60800026be40>{number = 4, name = (null)}
// 2017-03-28 17:10:40.614 07-GCD的基本使用[5361:272988] 3-----<NSThread: 0x60800026b9c0>{number = 5, name = (null)}
// 2017-03-28 17:10:40.615 07-GCD的基本使用[5361:272971] 1-----<NSThread: 0x60800026ba80>{number = 3, name = (null)}
// 2017-03-28 17:10:40.615 07-GCD的基本使用[5361:272973] 2-----<NSThread: 0x60800026be40>{number = 4, name = (null)}
}
- 同步函数 + 并发队列:不会开启新的线程
/**
* 同步函数 + 并发队列:不会开启新的线程
*/
- (void)syncConcurrent
{
// 1.获得全局的并发队列
//#define DISPATCH_QUEUE_PRIORITY_HIGH 2
//#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
//#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
//#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
// 2017-03-28 17:13:31.051 07-GCD的基本使用[5403:275128] 1-----<NSThread: 0x608000263940>{number = 1, name = main}
// 2017-03-28 17:13:31.053 07-GCD的基本使用[5403:275128] 2-----<NSThread: 0x608000263940>{number = 1, name = main}
// 2017-03-28 17:13:31.054 07-GCD的基本使用[5403:275128] 3-----<NSThread: 0x608000263940>{number = 1, name = main}
}
- 异步函数 + 串行队列:会开启新的线程,但是任务是串行的,执行完一个任务,再执行下一个任务
/**
* 异步函数 + 串行队列:会开启新的线程,但是任务是串行的,执行完一个任务,再执行下一个任务
*/
- (void)asyncSerial
{
// 1.创建串行队列
//#define DISPATCH_QUEUE_SERIAL NULL
dispatch_queue_t queue = dispatch_queue_create("wang", NULL);
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
// 2017-03-28 17:16:02.187 07-GCD的基本使用[5495:278209] 1-----<NSThread: 0x608000262440>{number = 3, name = (null)}
// 2017-03-28 17:16:02.187 07-GCD的基本使用[5495:278209] 2-----<NSThread: 0x608000262440>{number = 3, name = (null)}
// 2017-03-28 17:16:02.188 07-GCD的基本使用[5495:278209] 3-----<NSThread: 0x608000262440>{number = 3, name = (null)}
}
- 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
/**
* 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
*/
- (void)syncSerial
{
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("wang", DISPATCH_QUEUE_SERIAL);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
// 2017-03-28 17:17:25.275 07-GCD的基本使用[5527:279554] 1-----<NSThread: 0x6000000757c0>{number = 1, name = main}
// 2017-03-28 17:17:25.275 07-GCD的基本使用[5527:279554] 2-----<NSThread: 0x6000000757c0>{number = 1, name = main}
// 2017-03-28 17:17:25.275 07-GCD的基本使用[5527:279554] 3-----<NSThread: 0x6000000757c0>{number = 1, name = main}
}
- 异步函数 + 主队列:只在主线程中执行任务
/**
* 异步函数 + 主队列:只在主线程中执行任务
*/
- (void)asyncMain
{
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
// 2017-03-28 17:20:38.682 07-GCD的基本使用[5591:282861] 1-----<NSThread: 0x600000065a40>{number = 1, name = main}
// 2017-03-28 17:20:38.683 07-GCD的基本使用[5591:282861] 2-----<NSThread: 0x600000065a40>{number = 1, name = main}
// 2017-03-28 17:20:38.683 07-GCD的基本使用[5591:282861] 3-----<NSThread: 0x600000065a40>{number = 1, name = main}
}
- 同步函数 + 主队列:堵塞主线程,禁用
/**
* 同步函数 + 主队列:
*/
- (void)syncMain
{
NSLog(@"syncMain ----- begin");
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncMain ----- end");
}
7. GCD线程间的通信
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//执行异步操作
// 回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
//刷新UI
});
});
8. GCD延时执行
/**
* 延迟执行
*/
- (void)delay
{
//方法1
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
//方法2
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延迟执行");
});
//方法3
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
}
8. GCD只执行1次的代码dispatch_once
- (void)once
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只执行1次的代码dispatch_once");
});
}
9. GCD的dispatch_barrier栅栏
- (void)barrier
{
dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
// 2017-03-29 10:20:17.716 [1321:43448] ----1-----<NSThread: 0x608000264740>{number = 3, name = (null)}
// 2017-03-29 10:20:17.716 [1321:43722] ----2-----<NSThread: 0x60800007f7c0>{number = 4, name = (null)}
// 2017-03-29 10:20:17.716 [1321:43722] ----barrier-----<NSThread: 0x60800007f7c0>{number = 4, name = (null)}
// 2017-03-29 10:20:17.717 [1321:43722] ----3-----<NSThread: 0x60800007f7c0>{number = 4, name = (null)}
// 2017-03-29 10:20:17.717 [1321:43448] ----4-----<NSThread: 0x608000264740>{number = 3, name = (null)}
}
10. GCD的dispatch_apply
- (void)apply
{
NSMutableArray *arr = [NSMutableArray array];
for (int i=0; i<10; i++) {
[arr addObject:@(i)];
}
dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(arr.count, queue1, ^(size_t index) {
NSLog(@"%ld",index);
});
// 2017-03-29 10:44:58.489 [1877:73243] 2
// 2017-03-29 10:44:58.489 [1877:73244] 1
// 2017-03-29 10:44:58.489 [1877:73213] 0
// 2017-03-29 10:44:58.489 [1877:73246] 3
// 2017-03-29 10:44:58.489 [1877:73243] 4
// 2017-03-29 10:44:58.489 [1877:73244] 5
// 2017-03-29 10:44:58.489 [1877:73213] 6
// 2017-03-29 10:44:58.489 [1877:73246] 7
// 2017-03-29 10:44:58.489 [1877:73243] 8
// 2017-03-29 10:44:58.490 [1877:73244] 9
}
11. GCD的dispatch_group
- (void)group
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 创建一个队列组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"2");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"完成了");
});
// 2017-03-29 10:48:53.947 [1939:76156] 2
// 2017-03-29 10:48:53.947 [1939:76157] 1
// 2017-03-29 10:48:53.947 [1939:76157] 完成了
}
11. NSOperation的使用
- (void)blockOperation
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主线程
NSLog(@"下载1------%@", [NSThread currentThread]);
}];
// 添加额外的任务(在子线程执行)
[op addExecutionBlock:^{
NSLog(@"下载2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载4------%@", [NSThread currentThread]);
}];
[op start];
}
- (void)invocationOperation
{
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[op start];
}
- (void)run
{
NSLog(@"------%@", [NSThread currentThread]);
}
12. NSOperationQueue的使用
NSOperationQueue *queue = [[NSOperationQueue alloc] init];是可以开启线程的,串行和并行通过maxConcurrentOperationCount设置,为1的时候就是串行。[NSOperationQueue mainQueue]是在主线程执行任务。串行的
- (void)operationQueue1
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建操作(任务)
// 创建NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
// 创建NSBlockOperation
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3 --- %@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"download4 --- %@", [NSThread currentThread]);
}];
// 添加任务到队列中
[queue addOperation:op1]; // [op1 start]
[queue addOperation:op3]; // [op3 start]
}
- (void)operationQueue2
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
}
- (void)opetationQueue3
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 设置最大并发操作数
// queue.maxConcurrentOperationCount = 2;
queue.maxConcurrentOperationCount = 1; // 就变成了串行队列
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
}
- (void)suspend
{
if (self.queue.isSuspended) {
// 恢复队列,继续执行
self.queue.suspended = NO;
} else {
// 暂停(挂起)队列,暂停执行
self.queue.suspended = YES;
}
//取消任务
[self.queue cancelAllOperations];
}
12. 自定义NSOperation的使用
@interface MyOperation : NSOperation
@end
@implementation MyOperation
/**
* 需要执行的任务
*/
- (void)main
{
NSLog(@"需要执行的任务");
}
@end
13. 任务之间添加依赖addDependency
- (void) addDependency{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2----%@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3----%@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"download4----%@", [NSThread currentThread]);
}
}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download5----%@", [NSThread currentThread]);
}];
op5.completionBlock = ^{
NSLog(@"op5执行完毕---%@", [NSThread currentThread]);
};
// 设置依赖
[op3 addDependency:op1];
[op3 addDependency:op2];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
}
13. 线程之间的通信
/**
* 线程之间的通信
*/
- (void)test
{
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
}