为什么ParallelQuery <t>。转换为Observable时无效?</t>

时间:2010-02-18 10:51:48

标签: c# system.reactive parallel-extensions

我有一个可观察的集合,我想并行处理,然后在过滤时观察处理过的值,最后订阅一个接收过滤值的处理程序。

我的示例在语法上是正确的并且编译得很好,当我运行代码时,将评估执行过滤的Where语句。但没有数据通过订阅。如果我删除AsParallel以便通过常规IEnumerable完成处理,则数据会通过,并且一切都按预期工作。

这是我的示例,对字符串进行一些处理:

// Generate some data every second
var strings = Observable.Generate(() =>
    new TimeInterval<Notification<string>>(
        new Notification<string>
            .OnNext(DateTime.Now.ToString()), TimeSpan.FromSeconds(1)));

// Process the data in parallel
var parallelStrings = from value in strings.ToEnumerable().AsParallel()
                      select "Parallel " + value;

// Filter and observe
var data = String.Empty;
parallelStrings
    .Where(value => !String.IsNullOrEmpty(value))
    .ToObservable()
    .Subscribe(value => data = value);

下一个奇怪的事情是,如果我使用TakeWhile运算符,在我看来它在概念上类似于Where,观察ParallelQuery按预期工作:

// Filter and observe
var data = String.Empty;
parallelStrings
    .TakeWhile(cs => !String.IsNullOrEmpty(cs))
    .ToObservable()
    .Subscribe(value => data = value);

向订阅添加一些日志记录代码会显示收到的数据直到ToObservable转换,但不会在以下情况之后:

1.    var data = String.Empty;
2.    parallelStrings
3.        .Where(value => !String.IsNullOrEmpty(value))
4.        .Select(value => value)
5.        .ToObservable()
6.        .Select(value => value)
7.        .Subscribe(value => data = value);

第4行的lambda中的断点被击中,而第6行的lambda中的断点从未被击中。

为什么TakeWhile会使Where数据传入订阅者,而Where却没有?

如果它很重要,我在Visual Studio 2010 RC中开发我的代码,其项目目标是.Net 4.0 Framework Client Profile。

更新:基于@Sergeys answer我重新设计了var processedStrings = from value in strings let processedValue = "Parallel " + value where !String.IsNullOrEmpty(processedValue) select processedValue; var data = String.Empty; processedStrings .ToEnumerable() .AsParallel() .ToObservable() .Subscribe(value => data = value ); 过滤器的展示位置。以下代码按预期工作:

processedStrings

首先将初始可观察的{{1}}转换为可枚举的以便对其进行并行化,然后将其转换回observable以便订阅最终结果仍然感觉有点尴尬。

2 个答案:

答案 0 :(得分:2)

TakeWhile在概念上不等同于Where,因为它取决于排序。我怀疑查询是实际顺序执行(请参阅this blog post)。尝试在.WithExecutionMode(ParallelExecutionMode.ForceParallelism)示例中调用TakeWhile,我怀疑您会看到相同的结果。

我不知道为什么它不能在并行情况下工作...我可以建议你输入一些日志来查看数据到达的程度吗?您可以使用Select执行有用的日志记录,例如,在记录后返回原始项目。

答案 1 :(得分:2)

来自C# 4.0 in a Nutshell


PLINQ可以并行化的内容目前存在一些实际限制。这些 随后的Service Pack和Framework版本可能会放松限制。 以下查询运算符阻止查询被并行化,除非 源元素处于其原始索引位置:

  • Take,TakeWhile,Skip和SkipWhile
  • Select,SelectMany和ElementAt的索引版本

大多数查询运算符都会更改元素的索引位置(包括那些元素的索引位置) 删除元素,例如Where)。这意味着如果你想使用前面的 运营商,他们通常需要在查询的开头


因此,实际上,使用TakeWhile可以防止.AsParallel()并行化。很难说为什么哪里杀了订阅,但把它放在AsParallel 之前可能会解决问题。