iOS多线程
几种常用的多线程:
NSThread
可以直接控制线程对象,可以获取当前线程类,可以知道当前线程的各种属性。
用法:
//创建
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];
//启动
[thread start];
//创建并自动启动
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
//使用NSObject的方法创建并自动启动
[self performSelectorInBackground:@selector(run:) withObject];
GCD
他是苹果为多核的并行运算提出的解决方案,他会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。用的比较多。
任务和队列
任务:即操作,你想要干什么,说白了就是一段代码,在GCD中就是一个Blcok,所以添加任务十分方便。
任务有两种方式:同步执行和异步执行,他们之间的区别:
同步操作,会阻塞当前线程并等待block中的任务执行完毕,然后当前线程才会继续往下运行。
异步操作,当前线程会直接往下执行,不会阻塞当前线程。
队列:用于存放任务。一共有两种队列,串行队列和并行队列,他们的区别:
串行队列,放到串行队列的任务,GCD会FIFO
的取出来一个,执行一个,然后取下一个,这样一个一个的执行。
并行队列,放到并行队列的任务,GCD也会FIFO
的取出来,他取出一个就会放到别的线程,然后再取出一个又放到另一个线程。这样由于取的动作很快,忽略笔记,看起来所有的任务都是一起执行的。不过需要注意,GCD会根据系统资源控制并行数量,所以如果任务很多,他并不会让所有任务同时执行。
创建队列
- 主队列
//创建主队列
dispatch_queue_t queue = dispatch_get_main_queue();
- 自己创建的队列
其中第一个参数是标识符,用于DEBUG的时候标识唯一的队列,可以为空。第二个参数用来表示创建的队列是串行还是并行,传入DISPATCH_QUEUE_SERIAL
或NULL
表示创建串行队列。传入DISPATCH_QUEUE_CONCURRENT
表示创建并行队列。
//串行队列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
- 全局并行队列:只要是并行任务一般都加入到这个队列。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
创建任务
- 同步任务:会阻塞当前线程(SYNC)
dispatch_sync(<#queue#>,^{
//code here
NSLog(@"%@",[NSThread currentThread]);
});
- 异步任务:不会阻塞线程(ASYNC)
dispatch_async(<#queue#>,^{
//code here
NSLog(@"%@",[NSThread currentThread]);
});
队列组
队列组可以将很多队列添加到一个组里,这样的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。
//创建队列组
dispatch_group_t group = dispatch_group_create();
//创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//多次使用队列组的方法执行任务
dispatch_group_async(group, dispatch_get_main_queue(),^{
//code here
});
dispatch_group_async(group, queue,^{
//code here
});
//都完成后会自动通知
dispatch_group_notify(group,dispatch_get_main_queue(),^{
//code here
NSLog(@"完成-%@",[NSThread currentThread]);
});
NSOperation和NSOperationQueue
NSOperation是苹果公司对GCD的封装,NSOperation和NSOperationQueue分别对应GCD的任务
和队列
。
操作步骤:
- 将要执行的任务封装到一个
NSOperation
对象中。 - 将此任务添加到一个
NSOperationQueue
对象中。
然后系统就会自动执行任务。
添加任务
NSOperation只是一个抽象类,所以不能封装任务。但他有2个子类用于封装任务。分别是NSINvocationOperation
和1NSBlockOperation
。创建一个operation后,需要start
方法来启动任务,他会默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用cancel
方法即可。
NSInvocationOperation
//创建NSInvocationOperation对象
NSInvocationOperation * opertation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//开始执行
[operation start];
NSBlcokOperation
//创建NSBlcokOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@",[NSThread currentThtead]);
}];
//添加多个blcok
for (NSInteger i = 0;i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@",i,[NSThread currentThread]);
}];
}
//开始任务
[operation start];
创建队列
主队列
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];
NSOperationQueue
与GCD
的队列 相比较就会发现,这里没有串行队列,那如果我想要10个任务在其他线程串行的执行怎么办?
这就是苹果封装的妙处,你不用管串行、并行、同步、异步这些名词。NSOperationQueue 有一个参数 maxConcurrentOperationCount 最大并发数,用来设置最多可以让多少个任务同时执行。当你把它设置为 1 的时候,他不就是串行了嘛!
NSOperationQueue
还有一个添加任务的方法,- (void)addOperationWithBlock:(void (^)(void))block;
,这是不是和 GCD
差不多?这样就可以添加一个任务到队列中了,十分方便。
NSOperation
有一个非常实用的功能,那就是添加依赖。比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:
//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];