简体   繁体   English

RX:如何等待订户完成?

[英]RX: How to wait for subscribers to finish?

I have a producer which downloads data in pages from a Rest API and several consumers that process the pages (eg load them to a database). 我有一个生产者,它从Rest API下载页面中的数据,还有几个处理页面的消费者(例如,将它们加载到数据库中)。

I would like to have producer and consumers working in parallel, meaning that producer should not wait for a page to be consumed before downloading the next one. 我想让生产者和使用者并行工作,这意味着生产者在下载下一页之前不应该等待页面被消耗。 Each consumer needs to process pages sequentially. 每个使用者需要按顺序处理页面。

When all pages are downloaded, the main thread should wait for all consumers to complete their work (as consuming may take longer than producing). 下载所有页面后,主线程应等待所有使用者完成其工作(因为消耗可能比生产花费的时间更长)。

My current approach is as follows: 我当前的方法如下:

I have created an observable that downloads the pages, which starts as soon as consumer subscribers are attached. 我创建了一个可观察的页面,该页面可下载页面,该页面在附加了消费者订阅者后立即开始。 I configured the subscribers to observe on their own threads for parallel execution. 我将订阅服务器配置为在自己的线程上进行并行执行观察。

Code in C#: C#中的代码:

IEnumerable<Page> getPages = produce();
var observable = getPages.ToObservable().Publish();

observable
   .ObserveOn(NewThreadScheduler.Default)
   .Subscribe(page => consume1(page));

observable
   .ObserveOn(NewThreadScheduler.Default)
   .Subscribe(page => consume2(page));

observable.Connect();

The problem with this implementation is that the main thread may finish before all pages are processed and the application stops. 此实现的问题在于,主线程可能会在处理所有页面和应用程序停止之前完成。

How can I achieve this using RX? 如何使用RX实现呢?

Thanks! 谢谢!

Edit: 编辑:

Tried also the following approach (from an answer): 还尝试了以下方法(从答案中得出):

static void Main(string[] args)
{
    var getPages = Enumerable.Range(0, 10);

    var els1 = new EventLoopScheduler();
    var els2 = new EventLoopScheduler();

    var observable =
        getPages
            .ToObservable()
            .Publish(ps =>
                Observable
                    .Merge(
                        ps.Select(p => Observable.Start(() => consume1(p), els1)),
                        ps.Select(p => Observable.Start(() => consume2(p), els2))));

    observable.Wait();
}

public static void consume1(int p)
{
    Console.WriteLine($"1:{p}");
    Thread.Sleep(200);
}

public static void consume2(int p)
{
    Console.WriteLine($"2:{p}");
    Thread.Sleep(100);
}

observable.Wait() returns as soon as underlying enumerable finishes yielding values. observable.Wait()在基础可枚举完成产生值时立即返回。 The output is: 输出为:

1:0
2:0

Just to prove, if we replace getPages with: 只是为了证明,如果我们将getPages替换为:

var getPages = Enumerable.Range(0, 10)
    .Select(i =>
    {
        Console.WriteLine($"Produced {i}");
        Thread.Sleep(30);
        return i;
    });

then the output is: 那么输出是:

Produced 0
Produced 1
1:0
2:0
Produced 2
Produced 3
Produced 4
2:1
Produced 5
Produced 6
Produced 7
1:1
2:2
Produced 8
Produced 9

I think this does what you want: 我认为这可以满足您的需求:

var els1 = new EventLoopScheduler();
var els2 = new EventLoopScheduler();

var observable =
    getPages
        .ToObservable()
        .Publish(ps =>
            Observable
                .Merge(
                    ps.SelectMany(p => Observable.Start(() => consume1(p), els1)),
                    ps.SelectMany(p => Observable.Start(() => consume2(p), els2))));

I wrote this test code: 我写了这个测试代码:

var getPages = Enumerable.Range(0, 10);

var els1 = new EventLoopScheduler();
var els2 = new EventLoopScheduler();

var observable =
    getPages
        .ToObservable()
        .Publish(ps =>
            Observable
                .Merge(
                    ps.SelectMany(p => Observable.Start(() => consume1(p), els1)),
                    ps.SelectMany(p => Observable.Start(() => consume2(p), els2))));

observable.Wait();

public void consume1(int p)
{
    Console.WriteLine($"1:{p}");
    Thread.Sleep(200);
}

public void consume2(int p)
{
    Console.WriteLine($"2:{p}");
    Thread.Sleep(100);
}

I got this output: 我得到以下输出:

1:0
2:0
2:1
1:1
2:2
2:3
1:2
2:4
2:5
1:3
2:6
2:7
1:4
2:8
2:9
1:5
1:6
1:7
1:8
1:9

When you've finished with the EventLoopScheduler instances you should call .Dispose() on them to close the threads. 完成EventLoopScheduler实例后,应在它们上调用.Dispose()以关闭线程。

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

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