C# RX:如何等待用户完成?

C# RX:如何等待用户完成?,c#,language-agnostic,observable,reactive-programming,system.reactive,C#,Language Agnostic,Observable,Reactive Programming,System.reactive,我有一个生产者从RESTAPI下载页面中的数据,还有几个消费者处理页面(例如,将页面加载到数据库) 我希望生产者和消费者并行工作,这意味着生产者不应该等到一个页面被消费后再下载下一个页面。每个使用者都需要按顺序处理页面 当下载所有页面时,主线程应该等待所有使用者完成他们的工作(因为消费可能需要比生产更长的时间) 我目前的做法如下: 我已经创建了一个可以下载页面的可观察程序,它会在消费者订户连接后立即启动。我将订阅服务器配置为在 它们自己的线程用于并行执行 C#中的代码: IEnumerable

我有一个生产者从RESTAPI下载页面中的数据,还有几个消费者处理页面(例如,将页面加载到数据库)

我希望生产者和消费者并行工作,这意味着生产者不应该等到一个页面被消费后再下载下一个页面。每个使用者都需要按顺序处理页面

当下载所有页面时,主线程应该等待所有使用者完成他们的工作(因为消费可能需要比生产更长的时间)

我目前的做法如下:

我已经创建了一个可以下载页面的可观察程序,它会在消费者订户连接后立即启动。我将订阅服务器配置为在 它们自己的线程用于并行执行

C#中的代码:

IEnumerable getPages=product();
var observable=getPages.ToObservable().Publish();
可观察
.ObserveOn(NewThreadScheduler.Default)
.订阅(第=>1页);
可观察
.ObserveOn(NewThreadScheduler.Default)
.订阅(第=>2页);
连接();
这个实现的问题是,主线程可能在处理所有页面和应用程序停止之前完成

如何使用RX实现这一点

谢谢

编辑:

还尝试了以下方法(从答案):

static void Main(字符串[]args)
{
var getPages=可枚举的范围(0,10);
var els1=新的EventLoopScheduler();
var els2=新的EventLoopScheduler();
可观测var=
获取页面
.TooObservable()文件
.发布(ps=>
可观察
.合并(
ps.Select(p=>Observable.Start(()=>consumer1(p),els1)),
ps.Select(p=>Observable.Start(()=>consumer2(p),els2));
可观察的。等待();
}
公共静态void consumer1(int p)
{
Console.WriteLine($“1:{p}”);
睡眠(200);
}
公共静态无效用户2(int p)
{
Console.WriteLine($“2:{p}”);
睡眠(100);
}
observable.Wait()在基础枚举完成生成值时返回。输出为:

1:0
2:0
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
为了证明这一点,如果我们将getPages替换为:

var getPages=Enumerable.Range(0,10)
.选择(i=>
{
Console.WriteLine($“producted{i}”);
睡眠(30);
返回i;
});
那么输出是:

1:0
2:0
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

我想这正是你想要的:

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))));
我编写了以下测试代码:

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);
}
我得到了这个输出:

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 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
当您使用完
EventLoopScheduler
实例后,应该对它们调用
.Dispose()
,以关闭线程。

谢谢。不幸的是Wait()并没有等待消费者完成,页面也没有按顺序处理。还有其他想法吗?这似乎是一个非常基本的用例,尽管我无法想出一个解决方案。不幸的是,在我的情况下不是这样的-我的输出只显示了前两行。如果你增加睡眠时间,也许这对你来说也是一样的。使用System.Responsive版本3.1.1和4.1.5进行测试。@kra-您能解释一下在您的案例中它不起作用的意思吗?你试过我的示例代码了吗?它和我的样品一样有效吗?你的代码有什么不同?@kra-我有一个
Select
错误,应该是
SelectMany
。哎呀。这并不是由于可枚举完成,而是
IObservable
的外部可观测值产生了第一个值。并不是说它是可观察的,它工作正常。