多线程可以在充分发挥机器的性能,并行执行任务,可以根据自己的需要来创建同步线程还是异步线程,是否需要阻塞总线程等功能来达到提高效率的问题。当然线程数也不是越多越好,过多的线程数会消耗过多的CPU和内存资源,有可能会造成主线程的卡顿,影响体验,一般开四个同时运行的线程即可。
IOS一般使用这四种方式来多线程操作
performSelector
NSOperation
NSThread
GCD
一、NSObject的performSelector方法
一般只要是继承NSObject类的都有这个performSelector方法,可以很方便的设定推迟时间执行
//1.延迟多长时间执行 [self performSelector:@selector(log3:) withObject:@"Damon" afterDelay:2.0f];
当然也可以选择后台线程的模式,不同的模式的作用可以参考这个文章《NSRunLoop详解》
//2.或者下面这种带mode参数的,mode参数请看NSRunLoop详解 [self performSelector:@selector(log3:) withObject:@"Damon2" afterDelay:2.0f inModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
如果是在主线程,也就是UI线程,那么还可以设置是否阻塞主线程,等这个函数执行完毕之后才会去执行其他函数
//3.阻塞主线程,执行完毕之后才执行后面的其他函数 [self performSelectorOnMainThread:@selector(log3:) withObject:@"Damon" waitUntilDone:true];
如果不需要推迟时间,只是单纯的多线程运行,可以直接执行后台运行方案
//4.后台线程执行,异步执行 [self performSelectorInBackground:@selector(log3:) withObject:@"Hu"];
二、NSThread的使用
NSThread是 OS X 和 iOS 都提供的一个线程对象,它是线程的一个轻量级实现,但是这个是没有同步阻塞的功能的,也就是说是一个异步的,需要手动创建一个线程锁来管理线程。
比较常见的就是获取主线程和当前线程
[NSThread currentThread];//获取当前线程 [NSThread mainThread];//获取主线程
可以使用类方法直接创建一个线程执行
//1.直接类方法开启后台线程 [NSThread detachNewThreadSelector:@selector(log1) toTarget:self withObject:nil];
或者自己手动多创建几个线程各自执行
//2.使用成员方法 NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(log4:) object:@"Damon"]; [thread1 setName:@"thread1"];//设置线程名字 [thread1 start];//开始执行 NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(log4:) object:@"Hu"]; [thread2 setName:@"thread2"]; [thread2 start];
就像前面说的,他不支持同步的设置,所以如果需要同步执行创建的线程,就需要手动创建一个线程锁
//创建线程锁 self.m_lock = [[NSLock alloc] init];
在函数之内,自己手动管理线程锁的开关
-(void)log4:(id)obj { NSLog(@"log4"); static int i =10; [self.m_lock lock]; if (i>0) { i--; } [self.m_lock unlock]; }
三、NSOperation的使用
NSOperation还是很强大的,他是一个抽象类,支持手动设置线程数,多任务之间相互依赖的关系,暂停任务,取消任务等,在GCD之前应该是很推崇的。
如果只是一个后台线程,可以直接使用start方法运行即可
//1.直接开一个线程并使用 NSInvocationOperation *opera = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(log3:) object:@"Damon"]; [opera start];
如果是多个线程,可以使用block块创建任务,也可以使用alloc来创建任务,可以新创建线程去执行任务,也可以直接在主线程来执行任务,并且可以设置多个线程的相互依赖关系,所谓“依赖”关系,就是等待前一个任务完成后,后一个任务才能启动
//2.多个线程依赖 NSOperationQueue *quene = [[NSOperationQueue alloc] init]; NSInvocationOperation *opera1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(log3:) object:@"Damon"];//可以直接alloc NSBlockOperation *opera2 =[NSBlockOperation blockOperationWithBlock:^{ NSLog(@"opera2"); //可以使用块 }]; NSBlockOperation *opera3 =[NSBlockOperation blockOperationWithBlock:^{ NSLog(@"opera3"); //可以使用块 }]; [opera1 addDependency:opera2];//1依赖2,所谓“依赖”关系,就是等待前一个任务完成后,后一个任务才能启动 [opera3 addDependency:opera1];//3依赖1 [quene addOperation:opera2];//可以新线程里面加任务 [quene addOperation:opera1]; [[NSOperationQueue mainQueue] addOperation:opera3];//也可以在UI主线程里面加任务
NSOperation还可以管理最大线程数和任务的状态,前面说了,最大线程最好不要太大
[quene setMaxConcurrentOperationCount:4];//最大并发线程数 [quene setSuspended:YES];//暂停 [quene cancelAllOperations];//取消所有任务
四、GCD的使用
GCD是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行, Apple 公司宣称其在 GCD 技术中为更好地利用多核硬件系统做了很多的优化。所以,在性能方面 GCD 是不用担心的。而且 GCD 也提供了相当丰富的 API,几乎可以完成绝大部分线程相关的编程任务,所以这个是最受推崇的,在一个博客上说如果能用GCD的,最好都用GCD。
GCD是C语言的封装,开头都是dispatch。
1、dispatch_once
线程安全单一执行典型例子是单例,GCD 的 dispatch_once 能够保证传入的 block 被线程安全地唯一执行:
//1.只执行一次,可以用来构建类的单例 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"gcd"); });
多次来回点击执行,这个函数只会执行一次,只会输出一个gcd。
2、async异步,sync同步
函数的执行是按照异步还是同步调用来区分的,
同步执行
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
异步执行
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
而执行的函数的队列可以从全局获取,也可以获取主线程队列,或者手动创建队列
全局获取
//获取全局队列 dispatch_queue_t quene =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
主线程获取
dispatch_get_main_queue()
手动创建队列
dispatch_queue_t quene3 = dispatch_queue_create("Damon", nil);
然后根据是同步还是异步调用即可
//2.同步线程,阻塞主线程,执行完毕之后才继续往下执行 //global全局,queue队列,async异步,sync同步 //获取全局队列 dispatch_queue_t quene =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_sync(quene, ^{ NSLog(@"sync"); }); //3.异步 dispatch_queue_t quene2 =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(quene2, ^{ NSLog(@"async"); }); //在main主队列,只能同步 dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"main_quene,sync"); }); //创建队列 dispatch_queue_t quene3 = dispatch_queue_create("Damon", nil); dispatch_sync(quene3, ^{ NSLog(@"quene3"); });
当然还有可以根据时间推迟,推迟的话和NSObject里面的performSelector是等效的
//和[self performSelector:@selector(log1) withObject:nil afterDelay:5.0f];等价 dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)); dispatch_after(time, dispatch_get_main_queue(), ^(void){ [self log1]; });
五、总结
多线程是会用到的,总结下,最好就是多用GCD,然后就是NSOperation,还有其他的需要补充的话等后面碰到了再补充吧。
六、Demo下载
GitHub下载:https://github.com/DamonHu/IosThread
GitOsc下载:http://git.oschina.net/DamonHoo/IosThread
七、参考文章
版权属于:胡东东博客
本文链接:http://www.hudongdong.com/ios/349.html
转载本文以及大段采集进行后续编辑请注明参考本文标题和链接!否则禁止所有转载和采集行为!
自2017年12月26日起,文章内容底部均会出现该版权声明,对于忽视该声明隐藏原站链接违规转载和采集行为,将会对服务提供者进行版权投诉及赔偿!