当我们的APP在运行的时候就是一个进程,而进程启动的时候会创建一个线程,这个线程我们叫主线程。主线程一般用于更新UI,所以我们也可以叫做UI线程。一条线程只能做一件事情,当我们在下载资源或者播放音频的时候需要做其他事情而不想被打断的时候,就需要多个线程进行处理,这些线程叫子线程。
多线程开发分为以下三种(常见的)
一、NSThreadNSThread是轻量级的多线程开发,由于它是经过苹果封装的而且完全面向对象,所以我们用起来很直观。但是使用NSThread需要我们自己管理线程的生命周期,所以很少用到它。用的最多的就是获取当前线程信息。下面是它的一些用法
//创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(go:) object:nil];
//启动线程
[thread start];
//创建线程并自启动
[NSThread detachNewThreadSelector:@selector(come:) toTarget:self withObject:nil];
//获取当前线程
NSLog(@"%@",[NSThread currentThread]);
//获取主线程
NSLog(@"%@",[NSThread mainThread]);
//检查当前线程是否在执行,完成,或者取消
NSLog(@"%d",[NSThread currentThread].executing);
NSLog(@"%d",[NSThread currentThread].finished);
NSLog(@"%d",[NSThread currentThread].cancelled);
注意:如果上面创建的2个线程都实现了各自的方法并打印,会发现运行多次打印出来的顺序是不一样的。因为每个线程的实际执行顺序不一定按顺序执行。
二、GCD(Grand Central Dispatch)
GCD是用的比较多的一种多线程开发方式,它基于C语言开发,而且使用了block,所以使用起来比较方便,灵活。相比于NSThread,GCD不需要手动管理线程的生命周期。
在GCD中有2个非常重要的概念:任务、队列
任务:就是里面block执行的任务,分为同步和异步。同步,只有block里面的任务执行完成后,当前的线程才会运行(阻塞当前线程)。异步则是在执行任务的同时,当前线程会继续运行。
队列:用于存放任务,分为串行队列和并行队列。串行队列里面的任务,会根据FIFO(先进先出)的顺序执行。并行队列相比于串行,它是开多个线程一起执行(异步情况下)
//创建主队列(特殊的串行队列)
dispatch_queue_t queue = dispatch_get_main_queue();
//创建队列 第一个参数用于DEBUG的时候标识唯一的队列,可以为空。第二个参数表示串行或并行队列
//串行队列
dispatch_queue_t ACqueue = dispatch_queue_create("JL.GCD", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t BCqueue = dispatch_queue_create("JL.GCD", NULL);
//并行队列
dispatch_queue_t ABqueue = dispatch_queue_create("JL.GCD", DISPATCH_QUEUE_CONCURRENT);
//全局并行队列 系统提供的队列
dispatch_queue_t Bqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建任务
//同步任务 会阻塞当前线程
dispatch_sync(queue, ^{
NSLog(@"11111%@",[NSThread currentThread]);
});
//异步任务 不会阻塞线程(另外开线程)
dispatch_async(ACqueue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
当有多个队列时可以将其放到一组,GCD提供了这样一个组:队列组。当这个组内的队列任务执行完了之后会发一个通知给我们。
-(void)group{
//创建队列组
dispatch_group_t group = dispatch_group_create();
//创建队列
dispatch_queue_t Myqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//将队列加入队列组
dispatch_group_async(group, Myqueue, ^{
for (int i = 0; i<3; i++) {
NSLog(@"group-1 = %@",[NSThread currentThread]);
}
});
//将主队列加入队列组
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (int i = 0; i<5; i++) {
NSLog(@"main = %@",[NSThread currentThread]);
}
});
//组内队列执行完毕发出通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 %@",[NSThread currentThread]);
});
},
三、NSOperation
NSOperation是苹果公司对GCD的封装,完全面向对象,使用NSOperation进行多线程开发类似于C#中的线程池,只要将要执行的任务封装到一个NSOperation对象中(准确的来说是它的两个子类),然后再将此任务添加到NSOperationQueue中,线程就会依次启动。
1、NSInvocationOperation
-(void)invocationCreate{
//创建NSInvocationOperation对象
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(OP1:) object:nil];
//当NSInvocationOperation对象调用start方法启动的时候,改操作会在主线程中调用,一般都不会直接调用,而是添加到NSOperationQueue队列中。
// [invocationOperation start];
//创建NSOperationQueue队列
NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
//将NSInvocationOperation对象添加到队列中,此时队列会开启一个线程执行该操作
[operationQueue addOperation:invocationOperation];
}
-(void)OP1:(NSInvocationOperation *)sender{
NSLog(@"%@",[NSThread currentThread]);
}
2、NSBlockOperation
//创建blockOperation
-(void)blockCreate{
NSBlockOperation *blockOperation =[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block = %@",[NSThread currentThread]);
}];
//启动与NSInvocationOperation一样
// [blockOperation start];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
[operationQueue addOperation:blockOperation];
}
NSBlockOperation除了这个方法还有第二个方法,它可以给Operation添加多个block,这样Operation中的任务会并发执行,它会在主线程和其他线程执行。
-(void)blockCreate{
NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
//设置最大并发线程数
operationQueue.maxConcurrentOperationCount = 3;
for (int i = 0; i<3; i++) {
[operationQueue addOperationWithBlock:^{
NSLog(@" 第%d次 = %@",i,[NSThread currentThread]);
}];
}
}
使用NSBlockOperation,所有的操作都不需要单独自定义方法,同时解决了只能传递一个参数的问题;使用NSOperation可以设置最大并发线程,可以有效的对线程进行控制
NSOperation还有一个很重要的特点:依赖性。利用依赖控制线程的执行顺序。
-(void)dependency{
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@" 加载其他图片: %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"加载最后一张图片 : %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//blockOperation1依赖于blockOperation2,使得blockOperation1在blockOperation2执行后才执行
[blockOperation1 addDependency:blockOperation2];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
[operationQueue addOperations:@[blockOperation1,blockOperation2] waitUntilFinished:NO];
}
值得注意的是,如果设置了循环依赖的话,会造成死锁。导致线程不会被执行。如果需要解除依赖关系,可以使用removeDependency来解除
四、线程同步
多线程往往是多个线程并发执行的,同个资源可能被多个线程同时访问,造成资源抢夺。这个时候我们就需要采取一些措施。
@synchronized代码块:@synchronized中的代码执行时先检查同步对象是否被另一个线程占用,如果是便会处于等待状态,直到同步对象被释放。
@synchronized(self) {
//需要执行的代码块
}
本人对多线程的理解还不是很深刻,暂时就写到这,接下来会继续更新。