前言
iOS开发中,多线程的使用其实已经被简化得很容易了。使用得最多的应该是GCD,一个函数就可以开线程,往队列中加任务。不过实际的工程开发中,大家涉及到的如果是界面开发的话,对于线程的认识就只停留在这一步了,仅仅是为了不阻塞主线程而开另一个线程来做一些耗时操作。
但是如果进行了一些网络方面的开发,比如网络下载,文件上传,则需要对多线程有深入一些的理解,至少会使用一些互斥锁来防止多个线程重复修改临界资源。
本文旨在罗列iOS中各类多线程编程的知识。其中GCD和NSOperation我使用得最熟悉。
四种方法
- pthread
linux底层接口,可以各平台通用,但是需要手动管理线程的生命周期,使用起来难度高- NSThread
使用最多的是peformSelector以及currentThread- GCD
基于底层接口的封装,不过还是C语言的接口。动不动就使用,任务和队列是重点。- NSOperation & NSOperationQueue
基于GCD的封装,相比GCD有个特别明显的优点,那就是可以取消当前的操作,GCD是没办法取消的。
NSThread
初始化方法:
// 初始化线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(openThread:) object:nil];
// 开启线程
[thread start];
这时会调用openThread方法,这个方法就运行在thread这个线程中。
- (void)openThread:(id *)something {
NSThread *current = [NSThread currentThread];
NSLog(@"执行了openThread:方法,当前线程:%@", current);
}
还有其他开启线程的方法,比如
类方法开启
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
隐式创建
[self performSelectorInBackground:@selector(openThread:) withObject:nil];
阻塞当前线程
[NSThread sleepForTimeInterval:2];
在指定线程上执行操作
[self performSelector:@selector(openThread:) onThread:thread withObject:nil waitUntilDone:YES];
NSThread的开线程方式非常简单易用,系统已经帮我们省去了线程的创建、管理,销毁,我们只需要把注意力放在selector上,把指定任务的代码写完即可。
GCD
最重要的两个概念就是task和queue,任务和队列。你需要做的每一件事情就是一个任务。队列的存在让你可以执行多个任务。队列分串行队列和并行队列,串行就是每个任务按照先入先出挨个做,并行则不分先后一起做。
全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
主队列(串行)
dispatch_queue_t queue = dispatch_get_main_queue();
创建队列,第一个参数为队列名,第二个参数DISPATCH_QUEUE_SERIAL串行或DISPATCH_QUEUE_CONCURRENT并行
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
任务:同步执行
注意queue可以是串行或并行队列
dispatch_sync(queue, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
任务:异步执行
注意queue可以是串行或并行队列
dispatch_async(queue, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
NSOperation
- NSInvocationOperation
常见例子:initWithTarget: selector: object:,相当于performSelectorInBackground: withObject:- NSBlockOperation
常见方法:blockOperationWithBlock:,addExecutionBlock:(这货竟然能并发)- 自定义Operation
定制化的Operation,需要继承NSOperation以及覆写executing和finished
invocation和block的用法都非常简单,记住[operation start]
方法会让operation同步执行即可,如果要让其异步执行可自己开线程或者加入到NSOperationQueue中。
对于自定义一个Operation,这里可以仔细说明一下。如果你的Operation不用并发,那覆写(override, 下同)一个main函数和初始化函数就可以了。
记得最开始写的时候,一直不明白,在并发Operation的情况下,为什么要覆写executing和finished,还要覆写main或者start函数。查过了那么多的文档,总算在官方文档找到。
- 覆写start是因为,自定义的Operation需要手动调用其start方法来开始执行这个operation。所以你必须覆写这个方法以致于替换掉原来的执行环境。还有记住不能调用
[super start]
- main方法是可选的,作用和start方法类似,如果不实现start方法,默认调用的是main。
- executing和finish,operation有义务让调用者知道其执行的状态,所以操作执行的状态需要手动设置。
- isConcurrent/isAsychronous 鉴别此operation是否为并发操作
自定义Operation的代码这里就不给出来了,总之要自定义并发Operation的话就必须覆写上述几个方法。
NSOperationQueue
NSOperationQueue是设计来做NSOperation的并发操作用的,只要你调用了下面三个方法中的一个,你的Operation都会自动、并发执行。
[aQueue addOperation:anOp];
[aQueue addOperations:anArrayOfOps waitUntilFinished:NO];
[aQueue addOperationWithBlock:^{ }];
需要留意的地方是,NSOperationQueue的cancelAllOperations
方法,一旦调用,则会默认调用NSOperation的cancel,然后将其内部属性cancelled置为YES,如果这个Operation还未执行,那么有可能就被取消,而如果这个Operation正在执行中,那能不能取消,则结果未知。