iOS多线程GCD 、NSTread和NSOperation简介

在iOS开发中我们经常会用到多线程来处理一些业务,那么iOS里有哪些实现多线程的方式呢?

  • NSTread:封装程度最小、最轻量级,开销较大。
  • GCD(Grand Central Dispatch):内部效率优化,提供简洁的C语言接口,更加简单高效。
  • NSOperation:基于GCD的一个抽象基类,不需要管理线程的生命周期和同步,比GCD可控性强。
一、NSTread

NSTread封装程度最小、最轻量级的多线程编程接口,它使用更加灵活,但需要手动管理线程的生命周期、线程同步和线程加锁等,开销较大。

/** NSThread 静态工具方法 **/
    //1、是否开启了多线程
    BOOL isMultiThread = [NSThread isMultiThreaded];
    //2、获取当前线程
    NSThread *currentThread = [NSThread currentThread];
    //3、获取主线程
    NSThread *mainThread = [NSThread mainThread];
    //4、睡眠当前线程
    //4.1、线程睡眠5s
    [NSThread sleepForTimeInterval:5];
    //4.2、线程睡眠到指定时间,效果同上
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
    //5、退出当前线程,注意不要在主线程调用,防止主线程被kill掉
    [NSThread exit];

NSThread的使用比较简单,可以动态创建并初始化NSThread对象,对其进行设置并启动;也可以通过NSThread的静态方法快速创建并启动新线程;

/** NSTread 线程对象的基本创建,target为入口方法所在的对象,selector为线程入口方法 **/
    //1、线程实例对象创建与设置
    NSThread *newThread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    //设置线程优先级threadPriority(0~1.0),该属性即将被抛弃,将使用qualityOfService代替
    //newThread.threadPriority = 1.0;
    newThread.qualityOfService = NSQualityOfServiceUserInteractive;
    //开启线程
    [newThread start];
    //2、静态方法快速创建并开启新线程
    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"block run");
    }];

此外NSObject提供了隐式快速创建NSThread线程的performSelector系列方法。

/** NSObject 基类隐式创建线程的一些静态工具方法 **/
    //1、在当前线程上执行方法,延迟2s
    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
    //2、在指定线程上执行方法,不等待当前线程
    [self performSelector:@selector(run) onThread:newThread withObject:nil waitUntilDone:NO];
    //3、后台异步执行方法
    [self performSelectorInBackground:@selector(run) withObject:nil];
    //4、在主线程上执行方法
    [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
二、GCD

GCD(Grand Central Dispatch)又叫大中央调度,它对线程操作进行了封装,加入了很多新的特性,内部进行了效率优化,提供了简洁的C语言接口,使用更加简单高效,也是苹果公司推荐的方式。

GCD的用法特别灵活,在下一篇将详细讲解一下它的用法;这里主要掌握点分为以下几个方面:

1、串行队列与并发队列dispatch_queue_t;
/*
     创建队列
     DISPATCH_QUEUE_SERIAL 表示串行队列,队列内任务一个接一个的执行,按照先进先出(FIFO)的顺序执行
     DISPATCH_QUEUE_CONCURRENT 表示并发队列,队列内任务可同时并列执行,任务之间不会相互等待,执行顺序不可预测
     */
    // 串行队列的创建方法
    dispatch_queue_t queueSerial = dispatch_queue_create("com.jzsec.GCDtest", DISPATCH_QUEUE_SERIAL);
    // 并发队列的创建方法
    dispatch_queue_t queueCon = dispatch_queue_create("com.jzsec.GCDtest", DISPATCH_QUEUE_CONCURRENT);
2、同步dispatch_sync与异步dispatch_async派发任务;
dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
3、dispatch_once_t 只执行一次;
/**
 * 一次性代码(只执行一次)dispatch_once
 能保证某段代码在程序运行过程中只被执行1次,并且即使在多线程的环境下,dispatch_once也可以保证线程安全。
 */
- (void)once {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
        NSLog(@"dispatch_once");
    });
}
4、dispatch_after 延后执行;
/**
 * 延时执行方法 dispatch_after
 dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效的。
 */
- (void)after {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asyncMain---begin");
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2.0秒后异步追加任务代码到主队列,并开始执行
        NSLog(@"after---%@",[NSThread currentThread]);  // 打印当前线程
    });
}
5、dispatch_group_t 组调度;
/**
 * 队列组 dispatch_group_notify
 当group所有任务都执行完成之后,才执行dispatch_group_notify block 中的任务。
 */
- (void)groupNotify {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"group---begin");
    
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
        NSLog(@"group---end");
    });
}
三、NSOperation

基于GCD的一个抽象基类,将线程封装成要执行的操作,不需要管理线程的生命周期和同步,比GCD可控性强,例如加入操作依赖控制操作执行顺序、设置操作队列最大可并发执行的才做个数和取消执行等。

/** NSInvocationOperation 初始化 **/
    NSInvocationOperation *invoOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    [invoOperation start];
    
    /** NSBlockOperation 初始化 **/
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperationA:%@", [NSThread currentThread]);
    }];
    //blockOperation可以后续继续添加block执行块,操作执行后会在不同的线程并发执行这些执行块。
    [blockOperation addExecutionBlock:^{
        NSLog(@"NSBlockOperationB:%@", [NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"NSBlockOperationC:%@", [NSThread currentThread]);
    }];
    [blockOperation start];
    
    /** 获取主队列(主线程) **/
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
    //创建a、b、c操作
    NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"OperationA");
    }];
    NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"OperationB");
    }];
    NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"OperationC");
    }];
    //添加操作依赖,c依赖于a和b,这样c一定会在a和b完成之后才执行,即顺序为A、B、C
    [c addDependency:a];
    [c addDependency:b];
    //添加操作a、b、c 到操作队列queue(特意将c在a和b之前添加)
    [queue addOperation:c];
    [queue addOperation:a];
    [queue addOperation:b];
四、线程同步

对于UI的更新代码,必须要写在主线程上执行才会及时有效;当当前代码不在主线程时,需要将UI更新的部分代码单独同步到主线程。
同步的方法有三种:

  • NSThread类的performSelectorOnMainThread方法
  • NSOperation类的mainQueue主队列
  • GCD的dispatch_get_main_queue()获取主队列

推荐直接使用GCD的方法:

dispatch_async(dispatch_get_main_queue( ), ^{
     //刷新UI的代码
});

在iOS实际开发中,还是使用GCD的情况比较多,这里只是简单介绍对了三种多线程实现的方式,在下一篇将详细介绍一下GCD的用法和注意事项。

你可能感兴趣的:(iOS多线程GCD 、NSTread和NSOperation简介)