多线程可以在充分发挥机器的性能,并行执行任务,可以根据自己的需要来创建同步线程还是异步线程,是否需要阻塞总线程等功能来达到提高效率的问题。当然线程数也不是越多越好,过多的线程数会消耗过多的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/HudongBlogDemo/tree/master/IosThread
七、参考文章
- iOS- NSThread/NSOperation/GCD 三种多线程技术的对比及实现
- iOS学习笔记42—利用dispatch_once创建单例
- NSRunLoop详解
- OC多线程GCD以及NSoperation
- Cocoa API解説(macOS/iOS)
- OS X 和 iOS 中的多线程技术
- 关于iOS多线程,你看我就够了
- iOS中多线程原理与runloop介绍
版权属于:东哥笔记 - DongGe.org
本文链接:https://dongge.org/blog/349.html
自2017年12月26日起,『转载以及大段采集进行后续编辑』须注明本文标题和链接!否则禁止所有转载和采集行为!