使用GCD和NSOperation的自我感悟(部分用法和tips)

9 篇文章 0 订阅
6 篇文章 0 订阅

        GCD(Grand Central Dispatch):基于C语言,属于更底层的多线程技术,所以在效率上的优势比较明显;依赖于Block执行任务也使得开发上变得更简单,方便开发者看到任务的上下文。

        NSOperation和NSOperationQueue:其实出现得比GCD更早,但是GCD出现以后苹果在GCD的基础上对NSOperation进行了重写,使其对象化,符合了大众开发者的习惯。作为更高层的技术,NSOperation在处理依赖关系、控制各种操作、控制并发数量等方面都有一定的优势。(tips:NSOperation对于操作的暂停和取消并不是指当前操作可以马上停止和取消,而是当前操作结束以后会停止之后的所有操作。以前一度认为GCD不可以暂停和取消任务,后来发现并不是的,iOS8以后可以使用dispatch_block_cancel,开发者也可以自己用额外代码实现)

        博主作为一个四年多的老iOS程序开发,用到GCD和NSOperation的场景太多了,当然有人会说多线程技术也会用到NSThread,为什么不提及,因为NSThread需要自己控制线程的生命周期,用到的场景实在太少了,这里就不再赘述了。至于NSThread的原理和使用大家可以去网上查找。网上对于GCD和NSOperation的介绍和对比可以找出大堆文章,但是就博主自身而言,对实际开发遇到的难题能起到帮助的少之又少。经常会遇到的就是这篇文章推荐使用GCD,那篇文章又推荐使用NSOperation,导致开发者在实际开发中变得混乱,严重影响开发效率。     

        其实大部分情况下不管选择哪种技术都能达到预期的效果,无非是代码量和开发者个人理解的问题。就个人而言,以下几种情况可以优先考虑GCD:

(1)任务比较简单,需要集中代码的情况

(2)多个任务相互独立,不用考虑依赖关系和优先级关系的情况

(3)对代码执行效率要求比较高的情况

以下几种情况可以有限考虑NSOperation/NSOperationQueue:

(1)任务比较复杂,代码量庞大,集中代码会导致后期维护修改困难的情况

(2)几个任务之间有联系,相互依赖或者要求明确的优先级关系的情况

(3)需要自己维护任务,控制任务的暂停、取消的情况

(4)需要监听任务对象属性的情况(如监听NSOperation的isCancelled来判断任务是否取消)

 

话不多说,直接写代码,这里我们为了方便只用NSBlockOperation来对比

一、同步多任务,用sleep()模拟任务耗时,可以看到task1、task2、task3有序执行

GCD(同步任务使用并发和串行队列效果一致,只能在主队列中顺序执行)

    //并发队列
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    //串行队列
    //dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        sleep(2);
        NSLog(@"task1 %@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        sleep(1);
        NSLog(@"task2 %@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        sleep(3);
        NSLog(@"task3 %@", [NSThread currentThread]);
    });

NSOperation/NSOperationQueue(直接使用start()开始任务默认在主线程中顺序执行,或者使用NSOperationQueue设置队列并发数为1达到同步执行效果)

/// NSOperation同步执行任务
- (void)operationSync {
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(3);
        NSLog(@"task1 %@", [NSThread currentThread]);
    }];
    [operation1 start];
    
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(2);
        NSLog(@"task2 %@", [NSThread currentThread]);
    }];
    [operation2 start];
    
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(1);
        NSLog(@"task3 %@", [NSThread currentThread]);
    }];
    [operation3 start];
}

/// NSOperation和NSOperationQueue实现同步任务效果
- (void)operationQueueSync {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 设置并发数为1实现同步效果
    queue.maxConcurrentOperationCount = 1;
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(2);
        NSLog(@"task1 %@", [NSThread currentThread]);
    }];
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(1);
        NSLog(@"task2 %@", [NSThread currentThread]);
    }];
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(3);
        NSLog(@"task3 %@", [NSThread currentThread]);
    }];

    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
}

二、异步多任务

GCD(使用栅栏和GCD队列组的情况)

/**
 栅栏处理异步多任务,task1、task2、task3顺序不定,task4、task5顺序不定
 */
- (void)barrierAsync {
    //并行队列
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    //串行队列:若为串行队列,任务永远按顺序执行
    //dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"task1 %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"task2 %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"task3 %@", [NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"task barrier %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"task4 %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"task 5 %@", [NSThread currentThread]);
    });
}

/**
 GCD队列组处理多任务先执行,再进行后面任务,task1、task2、task3顺序不定,三个完成后执行task last
 */
- (void)groupQueue {
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
        NSLog(@"task1 %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"task2 %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"task3 %@", [NSThread currentThread]);
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"task last %@", [NSThread currentThread]);
    });
}

NSOperation/NSOperationQueue(使用依赖控制task1、task2、task3完成以后再执行lastTask,前三者顺序不定)

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *lastOperation = [NSBlockOperation blockOperationWithBlock:^{
        sleep(1);
        NSLog(@"@"lastTask %@", [NSThread currentThread]");
    }];
    NSBlockOperation *requestOperation1 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(4);
        NSLog(@"@"task1 %@", [NSThread currentThread]");
    }];
    NSBlockOperation *requestOperation2 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(2);
        NSLog(@"@"task2 %@", [NSThread currentThread]");
    }];
    NSBlockOperation *requestOperation3 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(3);
        NSLog(@"@"task3 %@", [NSThread currentThread]");
    }];
    [lastOperation addDependency:requestOperation1];
    [lastOperation addDependency:requestOperation2];
    [lastOperation addDependency:requestOperation3];
    [queue addOperation:lastOperation];
    [queue addOperation:requestOperation1];
    [queue addOperation:requestOperation2];
    [queue addOperation:requestOperation3];

三、处理多个异步任务中含异步操作的情况(多个网络请求以后刷新UI)

       这是实际开发中经常遇到的问题,多个网络请求获取不同的数据,但是每个接口的数据都是必须的,只有拿到所有的数据才能进行下面的操作,然而接口请求又是异步的。

GCD(GCD队列组的enter和leave,GCD队列组信号量)

/**
 GCD组多个异步网络请求返回数据以后再进行主线程的UI操作 enter和leave要成对出现
 */
- (void)groupAsyncRequest {
    dispatch_queue_t queue = dispatch_queue_create("asyncRequest", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
        dispatch_group_enter(group);
        //模拟网络请求
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(2);
            NSLog(@"task1 %@", [NSThread currentThread]);
            dispatch_group_leave(group);
        });
    });
    dispatch_group_async(group, queue, ^{
        dispatch_group_enter(group);
        //模拟网络请求
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(2);
            NSLog(@"task2 %@", [NSThread currentThread]);
            dispatch_group_leave(group);
        });
    });
    dispatch_group_async(group, queue, ^{
        dispatch_group_enter(group);
        //模拟网络请求
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(2);
            NSLog(@"task3 %@", [NSThread currentThread]);
            dispatch_group_leave(group);
        });
    });
    dispatch_group_notify(group, queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"task UI %@", [NSThread currentThread]);
        });
    });
}


/**
 GCD队列组用信号量处理多个异步网络请求
 */
- (void)semaphoreRequest {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("semaphore", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        //value表示可访问新生成总信号量个数
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        //模拟网络请求
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(2);
            NSLog(@"task1 %@", [NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });
    dispatch_group_async(group, queue, ^{
        //value表示可访问新生成总信号量个数
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        //模拟网络请求
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(2);
            NSLog(@"task2 %@", [NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });
    dispatch_group_async(group, queue, ^{
        //value表示可访问新生成总信号量个数
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        //模拟网络请求
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(2);
            NSLog(@"task3, %@", [NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });
    dispatch_group_notify(group, queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"task UI %@", [NSThread currentThread]);
        });
    });
}

NSOperation/NSOperationQueue

/// 网络请求数据返回以后再将相应队列任务加入到队列中
- (void)operationQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *lastOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"last task");
    }];
    NSBlockOperation *requestOperation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task1");
    }];
    NSBlockOperation *requestOperation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task2");
    }];
    NSBlockOperation *requestOperation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task3");
    }];
    [lastOperation addDependency:requestOperation1];
    [lastOperation addDependency:requestOperation2];
    [lastOperation addDependency:requestOperation3];
    [queue addOperation:lastOperation];
    
    [NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:@"https://www.csdn.net"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        [queue addOperation:requestOperation1];
    }];
    [NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        [queue addOperation:requestOperation2];
    }];
    [NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:@"https://github.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        [queue addOperation:requestOperation3];
    }];
}

 

       总的来说,不管是什么情况,我们都可以选择任何一种多线程技术。多数开发者是根据自身的开发习惯选择,比如博主就会在多数情况下选择GCD。之前也问过一些大神朋友,为什么很多国外论坛的朋友会选择NSOperation,得到的答案几乎是一样的,并不是谁优谁劣,需要自己取舍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值