美文网首页iOS线程
ios并发机制(四) —— NSOperation的串并行和操作

ios并发机制(四) —— NSOperation的串并行和操作

作者: 刀客传奇 | 来源:发表于2017-08-16 18:51 被阅读40次

    版本记录

    版本号 时间
    V1.0 2017.08.16

    前言

    信号量机制是多线程通信中的比较重要的一部分,对于NSOperation可以设置并发数,但是对于GCD就不能设置并发数了,那么就只能靠信号量机制了。接下来这几篇就会详细的说一下并发机制。感兴趣的可以看这几篇文章。
    1. ios并发机制(一) —— GCD中的信号量及几个重要函数
    2.ios并发机制(二) —— NSOperation实现多并发之创建任务
    3.ios并发机制(三) —— NSOperation实现多并发之创建队列和开启线程

    NSOperation串并行的实现

    NSOperation的串并行是通过设置最大并发数maxConcurrentOperationCount来实现的。

    • maxConcurrentOperationCount为1时,就是串行执行
    • maxConcurrentOperationCount > 1时,就是并行执行

    不管是串行还是并行,开启的线程数目是系统决定的,并不是我们决定的。

    1. 串行

    下面我们看一下代码。

    #import "JJSerialConquenceVC.h"
    
    @interface JJSerialConquenceVC ()
    
    @end
    
    @implementation JJSerialConquenceVC
    
    #pragma mark - Override Base Function
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        self.view.backgroundColor = [UIColor whiteColor];
        
        //创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        //设置并发数
        queue.maxConcurrentOperationCount = 1;
        
        [queue addOperationWithBlock:^{
            NSLog(@"1_thread = %@",[NSThread currentThread]);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"2_thread = %@",[NSThread currentThread]);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"3_thread = %@",[NSThread currentThread]);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"4_thread = %@",[NSThread currentThread]);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"5_thread = %@",[NSThread currentThread]);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"6_thread = %@",[NSThread currentThread]);
        }];
    }
    
    @end
    

    下面看输出结果

    2017-08-16 17:58:58.090660+0800 JJOC[10149:4513595] 1_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
    2017-08-16 17:58:58.090813+0800 JJOC[10149:4513595] 2_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
    2017-08-16 17:58:58.090868+0800 JJOC[10149:4513595] 3_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
    2017-08-16 17:58:58.090919+0800 JJOC[10149:4513595] 4_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
    2017-08-16 17:58:58.090967+0800 JJOC[10149:4513595] 5_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
    2017-08-16 17:58:58.091014+0800 JJOC[10149:4513595] 6_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
    

    可见,是串行执行的,而且系统只给开了一个线程。

    2. 并行

    下面我们看一下并行下的代码。

    #import "JJSerialConquenceVC.h"
    
    @interface JJSerialConquenceVC ()
    
    @end
    
    @implementation JJSerialConquenceVC
    
    #pragma mark - Override Base Function
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        self.view.backgroundColor = [UIColor whiteColor];
        
        //创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        //设置并发数
        queue.maxConcurrentOperationCount = 3;
        
        [queue addOperationWithBlock:^{
            NSLog(@"1_thread = %@",[NSThread currentThread]);
            sleep(1);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"2_thread = %@",[NSThread currentThread]);
            sleep(1);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"3_thread = %@",[NSThread currentThread]);
            sleep(1);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"4_thread = %@",[NSThread currentThread]);
            sleep(1);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"5_thread = %@",[NSThread currentThread]);
            sleep(1);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"6_thread = %@",[NSThread currentThread]);
            sleep(1);
        }];
    }
    
    @end
    

    看输出结果

    2017-08-16 18:02:38.893083+0800 JJOC[10154:4514418] 1_thread = <NSThread: 0x1742716c0>{number = 4, name = (null)}
    2017-08-16 18:02:38.893199+0800 JJOC[10154:4514414] 2_thread = <NSThread: 0x1700746c0>{number = 3, name = (null)}
    2017-08-16 18:02:38.893644+0800 JJOC[10154:4514415] 3_thread = <NSThread: 0x174271840>{number = 5, name = (null)}
    2017-08-16 18:02:39.897629+0800 JJOC[10154:4514414] 5_thread = <NSThread: 0x1700746c0>{number = 3, name = (null)}
    2017-08-16 18:02:39.897613+0800 JJOC[10154:4514418] 4_thread = <NSThread: 0x1742716c0>{number = 4, name = (null)}
    2017-08-16 18:02:39.898884+0800 JJOC[10154:4514415] 6_thread = <NSThread: 0x174271840>{number = 5, name = (null)}
    
    

    可以看见系统给开启了3个线程,执行顺序也变成了123546,说明是并行的。


    操作依赖

    有的时候我们希望执行完一个任务再去执行另外一个任务,这个时候就需要操作依赖了。比如说A、B两个操作,我们希望执行完A操作以后再去执行B操作,那么我们就可以设置B依赖于A,具体就是使用方法addDependency:

    下面还是直接看代码。

    #import "JJAddDependencyVC.h"
    
    @interface JJAddDependencyVC ()
    
    @end
    
    @implementation JJAddDependencyVC
    
    #pragma mark - Override Base Function
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        //创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
        NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1_thread = %@",[NSThread currentThread]);
            
        }];
        
        NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2_thread = %@",[NSThread currentThread]);
            
        }];
        
        [operation2 addDependency:operation1];
        
        [queue addOperation:operation1];
        [queue addOperation:operation2];
    }
    
    @end
    

    下面看输出结果

    2017-08-16 18:15:05.030277+0800 JJOC[10159:4515800] 1_thread = <NSThread: 0x17007d5c0>{number = 4, name = (null)}
    2017-08-16 18:15:05.030469+0800 JJOC[10159:4515800] 2_thread = <NSThread: 0x17007d5c0>{number = 4, name = (null)}
    

    可见,通过操作依赖实现了运行顺序的控制。

    下面说一点题外话,就是GCD中线程同步的实现。

    group实现

    #import "JJGCDGroupVC.h"
    
    @interface JJGCDGroupVC ()
    
    @end
    
    @implementation JJGCDGroupVC
    
    #pragma mark - Override Base Function
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        dispatch_queue_t queue = dispatch_queue_create(0, 0);
        dispatch_group_t group = dispatch_group_create();
        
        dispatch_group_enter(group);
        dispatch_group_async(group, queue, ^{
            NSLog(@"task 1 on %@",[NSThread currentThread]);
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        dispatch_group_async(group, queue, ^{
            NSLog(@"task 2 on %@",[NSThread currentThread]);
            dispatch_group_leave(group);
        });
        
        dispatch_group_notify( group, queue, ^{
            NSLog( @"all task done %@", [NSThread currentThread] );
            NSLog(@"可以执行其他的任务了");
        } );
    }
    
    @end
    

    下面看输出结果

    2017-08-16 18:35:32.894210+0800 JJOC[10163:4518939] task 1 on <NSThread: 0x174079900>{number = 4, name = (null)}
    2017-08-16 18:35:32.894343+0800 JJOC[10163:4518939] task 2 on <NSThread: 0x174079900>{number = 4, name = (null)}
    2017-08-16 18:35:32.894393+0800 JJOC[10163:4518939] all task done <NSThread: 0x174079900>{number = 4, name = (null)}
    2017-08-16 18:35:32.894424+0800 JJOC[10163:4518939] 可以执行其他的任务了
    

    可见开启了线程并实现了任务的同步。

    信号量semaphore实现

        dispatch_semaphore_t sem =     dispatch_semaphore_create(0);
        [networkManager requestWithDelay:5 completion:^{
            dispatch_semaphore_signal(sem);//+1
        }];
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);//-1
        NSLog(@"OK");
    

    信号量方面的知识,前几篇已经说过了,这里很清晰的是等待网络请求结束,才可以行NSLog(@"OK");这段代码。

    barrier

    barrier阻塞的使用起来就很方便了,它就相当于一个栅栏,将不同的任务块区分开来,如下图所示。

    barrier

    这里需要主要的是,barrier只能用于并发队列不能用于全局队列。

    下面看代码。

    #import "JJGCDBarrierVC.h"
    
    @interface JJGCDBarrierVC ()
    
    @end
    
    @implementation JJGCDBarrierVC
    
    #pragma mark - Override Base Function
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        //创建并发队列
        dispatch_queue_t queue = dispatch_queue_create("cc.imguiqing", DISPATCH_QUEUE_CONCURRENT);
        
        //开启异步任务
        
        dispatch_async(queue, ^{
            NSLog(@"task 1 on %@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"task 2 on %@",[NSThread currentThread]);
        });
        
        dispatch_barrier_async(queue, ^{
            NSLog(@"barrier ==========");
        });
        
        dispatch_async(queue, ^{
            NSLog(@"task 3 on %@",[NSThread currentThread]);
        });
    }
    
    @end
    

    下面看输出结果

    2017-08-16 18:49:07.271659+0800 JJOC[10166:4521475] task 1 on <NSThread: 0x174078380>{number = 3, name = (null)}
    2017-08-16 18:49:07.271736+0800 JJOC[10166:4521478] task 2 on <NSThread: 0x170261b80>{number = 4, name = (null)}
    2017-08-16 18:49:07.271761+0800 JJOC[10166:4521478] barrier ==========
    2017-08-16 18:49:07.271799+0800 JJOC[10166:4521478] task 3 on <NSThread: 0x170261b80>{number = 4, name = (null)}
    

    这个很好理解就不多说了。


    一些其他方法

    1. - (void)cancel;

    • NSOperation类中的方法,取消单个操作,这里的取消不是立即取消操作,而是当前的操作完成以后不再继续执行新的操作。

    2. - (void)cancelAllOperations;

    • NSOperationQueue中的方法,取消队列中的所有操作。

    3. @property (getter=isSuspended) BOOL suspended;

    • NSOperationQueue中只读属性,用来判断队列是否暂停。

    上面所说的名词,暂停和取消是有区别的,暂停的意思就是操作以后还可以回复,而取消的意思就是所有的操作清空,不能继续执行其他的剩下的操作了。

    后记

    未完,待续~~~

    相关文章

      网友评论

        本文标题:ios并发机制(四) —— NSOperation的串并行和操作

        本文链接:https://www.haomeiwen.com/subject/wexwrxtx.html