简体   繁体   English

Observable.ObserveOn()似乎没有效果

[英]Observable.ObserveOn() appears to have no effect

I am trying to use use Rx to process items in parallel. 我试图使用Rx并行处理项目。 It seems I can't tell Rx to run my observer's OnNext() in parallel. 似乎我不能告诉Rx并行运行我的观察者的OnNext()。 Here is test code to demonstrate 这是测试代码来演示

[Test]
public void ObservableObserveOnNewThreadRunsInParallel()
{
    Console.WriteLine("Starting thread: {0}", Thread.CurrentThread.ManagedThreadId);

    // store items as they are output
    var list = new List<Tuple<string, int, int, int, TimeSpan>>();

    // used to wait until a sequences are complete
    var ev = new AutoResetEvent(false);

    // try these schedulers
    var schedulers = new[] {
                                Tuple.Create("ThreadPoolScheduler.Instance", (IScheduler)ThreadPoolScheduler.Instance),
                                Tuple.Create("NewThreadScheduler.Default", (IScheduler)NewThreadScheduler.Default),
                                Tuple.Create("TaskPoolScheduler.Default", (IScheduler)TaskPoolScheduler.Default),
                                Tuple.Create("Scheduler.Default", (IScheduler)Scheduler.Default),
                                Tuple.Create("Scheduler.Immediate", (IScheduler)Scheduler.Immediate),
                            };

    // try each scheduler
    foreach (var schedulerTuple in
        schedulers) {

        // emit tuples <i, delay> where delay decreases with each new tuple
        // such that output timing is expected to be reversed
        var observable =
            Observable.Range(0, 5)
                .Select(i => Tuple.Create((int)i, (int)(500 - i * 100)))
                .Take(5);

        var dt = DateTime.Now;
        Tuple<string, IScheduler> scheduler = schedulerTuple;


        observable
            // specify the scheduler to use
            .ObserveOn(schedulerTuple.Item2)
            .Subscribe(
                v => {
                    // emulate some work (first items take longer than last items)
                    Thread.Sleep(v.Item2);

                    // record when the item is done recording
                    lock (list)
                        list.Add(
                            Tuple.Create(
                                scheduler.Item1,
                                v.Item1,
                                v.Item2,
                                Thread.CurrentThread.ManagedThreadId,
                                dt - DateTime.Now));
                },
                // let the test go on
                () => ev.Set());

        // wait until the end of the sequence
        ev.WaitOne();
    }

    // print observed order
    foreach (var i in list) {
        Console.WriteLine(i);
    }
}

And the output : 并输出:

Starting thread: 5
(ThreadPoolScheduler.Instance, 0, 500, 9, -00:00:04.2514251)
(ThreadPoolScheduler.Instance, 1, 400, 9, -00:00:04.6524652)
(ThreadPoolScheduler.Instance, 2, 300, 9, -00:00:04.9524952)
(ThreadPoolScheduler.Instance, 3, 200, 9, -00:00:05.1525152)
(ThreadPoolScheduler.Instance, 4, 100, 9, -00:00:05.2525252)
(NewThreadScheduler.Default, 0, 500, 11, -00:00:06.5066506)
(NewThreadScheduler.Default, 1, 400, 11, -00:00:06.9066906)
(NewThreadScheduler.Default, 2, 300, 11, -00:00:07.2067206)
(NewThreadScheduler.Default, 3, 200, 11, -00:00:07.4067406)
(NewThreadScheduler.Default, 4, 100, 11, -00:00:07.5067506)
(TaskPoolScheduler.Default, 0, 500, 12, -00:00:00.5020502)
(TaskPoolScheduler.Default, 1, 400, 12, -00:00:00.9020902)
(TaskPoolScheduler.Default, 2, 300, 12, -00:00:01.2021202)
(TaskPoolScheduler.Default, 3, 200, 12, -00:00:01.4021402)
(TaskPoolScheduler.Default, 4, 100, 12, -00:00:01.5021502)
(Scheduler.Default, 0, 500, 13, -00:00:00.5020502)
(Scheduler.Default, 1, 400, 13, -00:00:00.9020902)
(Scheduler.Default, 2, 300, 13, -00:00:01.2021202)
(Scheduler.Default, 3, 200, 13, -00:00:01.4021402)
(Scheduler.Default, 4, 100, 13, -00:00:01.5021502)
(Scheduler.Immediate, 0, 500, 5, -00:00:00.5020502)
(Scheduler.Immediate, 1, 400, 5, -00:00:00.9040904)
(Scheduler.Immediate, 2, 300, 5, -00:00:01.2041204)
(Scheduler.Immediate, 3, 200, 5, -00:00:01.4041404)
(Scheduler.Immediate, 4, 100, 5, -00:00:01.5041504)

Notice how each OnNext call appear to have waited on the previous call even though I explicitly use ObserveOn() to specify the scheduler to use for notifications. 注意每个OnNext调用看起来如何等待上一次调用,即使我明确使用ObserveOn()来指定用于通知的调度程序。

I expected all but Scheduler.Immediate to run the notifications in parallel. 我希望除了Scheduler.Immediate之外的所有人都可以并行运行通知。

Anyone know what I am doing wrong ? 谁知道我做错了什么?

This is by design. 这是设计的。 One of Rx's primary contracts is that all notifications must be serialized. Rx的主要合同之一是必须序列化所有通知。

See §§4.2, 6.7 in the Rx Design Guidelines . 参见Rx设计指南中的§§4.2,6.7。

Observables represent concurrency in Rx, so to have overlapping notifications requires two or more observables. Observable表示Rx中的并发性,因此重叠通知需要两个或多个observable。 Notifications won't overlap in the same observer, but they'll overlap with respect to each observer. 通知不会在同一观察者中重叠,但它们将与每个观察者重叠。

For instance, if you need to execute two methods (observers) concurrently, then you need to define two observables. 例如,如果需要同时执行两个方法(观察者),则需要定义两个observable。

Technically, it's the observers (subscriptions) not the observables that are needed for concurrency; 从技术上讲,它是观察者(订阅)而不是并发所需的可观察量; therefore, subscribing to the same cold observable twice can produce concurrency, depending upon the scheduler used by the observable; 因此,根据观察者使用的调度程序,订阅两次相同的可观察量可以产生并发性; however, subscribing to the same hot observable twice does not result in concurrency. 但是,两次订阅相同的可观察量并不会导致并发。 (See my blog post: Hot and Cold Observables .) (参见我的博文: Hot and Cold Observables 。)

ObserveOn introduces concurrency when passed a concurrency-introducing scheduler. ObserveOn在传递并发引入调度程序时引入了并发性。 But how can it do that without violating the §6.7 contract? 但是如果不违反§6.7合同怎么办呢? Well, it splits the observable into two observables: before the operator and after the operator! 好吧,它将观察者分成两个可观察量: 操作员之前和操作员之后 Alternatively, you can look at it as two subscriptions or observers: before and after . 或者,您可以将其视为两个订阅者或观察者: 之前之后 The before observer is an internal observer that ObserveOn provides. 观察者是ObserveOn提供的内部观察者。 The after observer is your observer, or the observer provided by the next operator in the query. after观察者是您的观察者,或查询中下一个运算符提供的观察者。

No matter how you look at it, the notifications in the before observable can occur concurrently with respect to the notifications in the after observable. 无论你如何看待它, 之前可观察的通知可以与after observable中的通知同时发生。 But the after observer will only receive serialized notifications in the context of the after observable. 但是观察者只会在after observable的上下文中接收序列化通知。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM