C# 转换IEnumerable<;T>;至可观察<;T>;,以最大的平行度

C# 转换IEnumerable<;T>;至可观察<;T>;,以最大的平行度,c#,async-await,system.reactive,C#,Async Await,System.reactive,我有一系列异步任务要做(比如,获取N个网页)。现在我想把它们全部公开为一个IObservable。我当前的解决方案使用以下答案: 异步任务GetPage(字符串页){ 控制台。写入线(“之前”); var结果=等待从Internet获取(第页); 控制台。写入线(“之后”); 返回结果; } //pages是一个IEnumerable IObservable resultObservable=pages.Select(GetPage)。 选择(t=>Observable.fromsync(()=

我有一系列异步任务要做(比如,获取N个网页)。现在我想把它们全部公开为一个
IObservable
。我当前的解决方案使用以下答案:

异步任务GetPage(字符串页){ 控制台。写入线(“之前”); var结果=等待从Internet获取(第页); 控制台。写入线(“之后”); 返回结果; } //pages是一个IEnumerable IObservable resultObservable=pages.Select(GetPage)。 选择(t=>Observable.fromsync(()=>t)).Merge(); //现在使用列表 foreach(resultObservable.ToEnumerable()中的ResultObj){ Console.WriteLine(obj.ToString()); } 问题是我不知道要获取的页面数量,而且可能会很大。我不想同时提出数百个请求。所以我想要一种方法来限制并行执行的最大任务数。有没有办法限制
GetPage
的并发调用次数

有一个采用maxConcurrent参数的
Merge
重载,但它似乎并没有实际限制函数调用的并发性。控制台将在After消息之前打印所有Before消息

注意:我需要转换回
IEnumerable
。我正在为一个系统编写一个数据源,该系统为我提供要获取的数据描述符,我需要将下载数据的列表返回给它。

编辑

以下几点应该行得通。限制并发订阅的数量

var resultObservable = pages
  .Select(p => Observable.FromAsync(() => GetPage(p)))
  .Merge(maxConcurrent);
解释 为了理解为什么需要这种改变,我们需要一些背景知识

  • fromsync
    返回将调用传递的
    Func
    的可观察对象。这意味着,如果从未订阅过observable,则永远不会调用它

  • Merge
    急切地读取源序列,同时只订阅
    n
    可观测值

  • 通过这两个部分,我们可以知道为什么原始版本将并行执行所有操作:因为(2),
    GetPage
    将在
    Merge
    决定需要订阅多少个观测值时,已经为所有源字符串调用了
    GetPage


    我们还可以看到第二个版本工作的原因:即使序列已经被完全迭代,(1)意味着
    GetPage
    Merge
    决定需要订阅
    n
    可观测对象之前不会被调用。这只会导致同时执行
    n
    任务的预期结果

    我编辑了这个问题。我试过那个版本,但似乎不起作用:所有的任务都是同时触发的。@felipe现在修复了它。我明白了,这是有效的。你能解释一下原因吗?我想我理解为什么我需要在Observable.fromsync中调用GetPage方法,但我不确定。当你的解释是大部分工作时,我对此感到内疚:)事实上,@JeffMercado帮助我看到了正确的方向,你意识到你在创建Observable之前就已经完成了任务,对吗?您的
    .Select(GetPage)
    调用将过早地取消您的任务。@JeffMercado否,linq使用延迟执行,因此Select(GetPage)不会触发任何任务。它会<代码>选择(GetPage)在枚举时执行。通过在末尾调用
    Merge()
    ,您正在枚举可观察对象的集合
    Select(t=>Observable.fromsync(()=>t))
    ,该集合是由
    Select(GetPage)
    投影创建的任务创建的。在创建可观察对象之前,您已经有效地启动了任务。如果没有merge调用,那么是的,它就不会有了,但是既然您这样做了…@jeffmerca,那么罪魁祸首就是
    merge
    ,而不是
    Select
    :)。所以被接受的答案所解决的问题是Merge将急切地迭代源序列?XML文档不建议这样做。好吧,不一定是
    合并
    ,而是两个选择。Merge期望一个可枚举的观察对象,这些观察对象将观察一个任务,并将它们全部拉入以创建一个单独的观察对象。但是,这些可观察对象中的每一个都是从第一个select创建的已经存在(并且正在运行)的任务中创建的。
    var resultObservable = pages
      .Select(p => Observable.FromAsync(() => GetPage(p)))
      .Merge(maxConcurrent);