IOS多线程使用

多线程可以在充分发挥机器的性能,并行执行任务,可以根据自己的需要来创建同步线程还是异步线程,是否需要阻塞总线程等功能来达到提高效率的问题。当然线程数也不是越多越好,过多的线程数会消耗过多的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主线程里面加任务

屏幕快照 2016-08-29 18.47.53.png

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。

屏幕快照 2016-08-29 21.02.39.png

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

七、参考文章

Last modification:January 1st, 1970 at 08:00 am
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

Leave a Comment