美文网首页
ReactiveCocoa技术讲解-第五讲并发编程

ReactiveCocoa技术讲解-第五讲并发编程

作者: 好雨知时节浩宇 | 来源:发表于2017-12-20 22:43 被阅读101次

    同步 && 异步

    同步:函数调用,不返回结果不进行下一步。
    异步:函数调用,直接进行下一步,通过回调函数返回结果。

    并发 && 并行

    并发:在一个物理计算核心(cpu),通过调度手段兼顾多个任务,使任务看似一起执行。
    图示:

    并发.png

    并行:在多个物理计算核心(cpu),通过分配手段处理多个任务,使任务一起执行。
    并行图示:


    屏幕快照 2017-12-20 下午9.37.08.png

    注意:
    并发任务从宏观上来看:十多个任务同时被执行了, 但是微观上来说还是串行执行的。这点看图大家会很清楚。

    RACScheduler 使用

    1、创建scheduler:

    RACscheduler的6个类方法.png
    2、执行任务:
    执行任务.png
    注意:取消任务执行操作dispose是一个伪取消,后面会明为什么是个伪取消。

    RACScheduler同GCD对比

    通过上面的操作我们看到RACScheduler同GCD在代码书写和使用上有很大的相似之处,那二者之间关系到底有没有关系呢?下面就详细对比下。

    1、RACScheduler 是对GCD的高级封装,它是使用GCD来实现的。
    2、RACScheduler创建的任务是可以“取消”的,当然这个取消是一个伪取消,原因是:取消后,实际上仍然会执行,只不过执行时回调函数没有调用而已。因为RACScheduler是基于GCD实现的,而GCD中并没有取消操作,所以RACScheduler只能是一个高级的封装。
    3、一个scheduler中的任务是一定是串行执行的。(如果需要两个任务同时执行,则可以创建2个scheduler)。
    4、同一个scheduler中的任务不能保证是同一条线程来执行。
    GCD的串行queue,不能保证执行的线程是同一个。线程字典,现场堆栈,如果不能保证是同一条线成,线程字典写入时会有问题。同样schedule也是不能保证线程是同一个需要注意。

    信号订阅的顺序:(多线程中信号订阅的执行逻辑):

    1、普通的信号订阅执行过程:
    2、异步订阅

    - (void)subscriberAsync {
        RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {\
            NSLog(@"111");
            [subscriber sendNext:@"1"];
            [subscriber sendCompleted];
            return nil;
        }];
        [[RACScheduler scheduler] schedule:^{ //异步订阅
            NSLog(@"222");
            [signal subscribeNext:^(id x) {
                NSLog(@"333");
            }];
        }];
        NSLog(@"444");
    }
    //console:444 - 222 - 111 - 333
    

    3、异步发送

    - (void)sendAsync {
        RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {\
            NSLog(@"111");
            RACDisposable *disposable = [[RACScheduler scheduler] schedule:^{
                [subscriber sendNext:@"1"];
                [subscriber sendCompleted];
            }];
            return disposable;
        }];
        NSLog(@"222");
        [signal subscribeNext:^(id x) {
            NSLog(@"333");
        }];
        NSLog(@"444");
    
    }
    //console: 222 -> 111 -> 444 ->333
    

    4、同步订阅,异步发送

    - (void)sendEverywhere {
        //在不同的schedule发送
        RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {\
            NSLog(@"111");
            [subscriber sendNext:@0.1];
            RACDisposable *disposable = [[RACScheduler scheduler] schedule:^{
                [subscriber sendNext:@1.1];
                [subscriber sendCompleted];
            }];
            return disposable;
        }];
        NSLog(@"222");
        [signal subscribeNext:^(id x) {
            NSLog(@"%@",x);
        }];
        NSLog(@"444");
    }
    //console:2 1 0.1 444
    //console: 1.1
    

    5、异步订阅,异步发送

    - (void)sendAsyncAndsubscruberAsync {
        //在不同的schedule发送
        RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {\
            NSLog(@"111");
            [subscriber sendNext:@0.1];
            RACDisposable *disposable = [[RACScheduler scheduler] schedule:^{
                [subscriber sendNext:@1.1];
                [subscriber sendCompleted];
            }];
            return disposable;
        }];
        
        [[RACScheduler scheduler] schedule:^{
            NSLog(@"222");
            [signal subscribeNext:^(id x) {
                NSLog(@"%@",x);
            }];
        }];
        NSLog(@"444");
    }
    

    信号订阅执行逻辑

    1)subscribeNext:永远和didSubscriber block绑在一起。也就是只要调用subscribeNext:,紧接着一定会执行这个block。
    2) 在didSubscriber这个block中执行sendNext,sendComplete会立即在当前线程执行订阅者传过来的block(也就是subscribeNext:^{ //代码 } 这个block)。
    尤其是要注意的是:在当前线程中执行subscribeNext block

    总结:上面的4中情况(出去1),我们会发现信号的返回值可能在出现不同的线程中,这就很难控制,例如我们有一个网络请求,我们异步发起一个网络请求,然后让线程常驻以便接受请求返回值,如果出现上面的情况,网络请求的返回值跑到了另一条线程上去。这就可能不符合我们的预期。从而出现一些异常。
    为了解决上面的问题,让信号返回值在指定的线程中返回,RAC挺高了两种解决办法:
    1、subscriberOn: 在指定的线程执行didsubscriber这个block

    (1)使用subscriberOn:后,信号订阅以及信号返回值所在线程的例子:

    subscriberOn:.png
    (2)subscriberOn:只能保证didsubscriber block在指定的scheduler执行,但是不能保证sendNext 、sendComplete在哪个scheduler执行。
    注:didsubscriber这个block 是指:创建信号过程中,执行信号订阅 的代码块

    2、deliverOn: 在指定的线程接受信号返回值。

    (1)使用deliverOn:后的信号值所在的线程图示:

    deliverOn.png
    步骤讲解上图:
    1)在执行deliverOn:subscribeNext:后,会立即执行didSubscribe这个block。注意这里同subscribeOn:不同
    2)执行sendNext时:通常情况下,会在当前线程中立即执行订阅者发送过来的block,但是由于使用了deliverOn:,它实际上是将订阅者的block放到了指定线程的schedule中。所以这里会稍后在指定的线程中执行这个订阅者block(上图这里就是mainThreadScheduler)。
    3)新创建的schedule中执行sendNext:同样会在主线程中执行订阅者的block,因为订阅者是同一个,他提供的block同样被放到指定线程的schedule中。
    4)注意:主线程中的0.1前面的“scheduler计划表”是由[RACScheduler mainThreadScheduler]创建的一个新的计划表。下面的线程是由中间最后一个schedule创建的。(当线程存在时,只会往线程中添加一个scheduler,当线程不存在时,会先创建一条线程,然后添加schedule).一个schedule是一个串行queue,它是基于GCD封装的。

    相关文章

      网友评论

          本文标题:ReactiveCocoa技术讲解-第五讲并发编程

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