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

2017-12-20  本文已影响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封装的。
上一篇下一篇

猜你喜欢

热点阅读