我有一个可观察的集合,我想并行处理,然后在过滤时观察处理过的值,最后订阅一个接收过滤值的处理程序。
我的示例在语法上是正确的并且编译得很好,当我运行代码时,将评估执行过滤的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以便订阅最终结果仍然感觉有点尴尬。
答案 0 :(得分:2)
TakeWhile
在概念上不等同于Where
,因为它取决于排序。我怀疑查询是实际顺序执行(请参阅this blog post)。尝试在.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
示例中调用TakeWhile
,我怀疑您会看到相同的结果。
我不知道为什么它不能在并行情况下工作...我可以建议你输入一些日志来查看数据到达的程度吗?您可以使用Select执行有用的日志记录,例如,在记录后返回原始项目。
答案 1 :(得分:2)
PLINQ可以并行化的内容目前存在一些实际限制。这些 随后的Service Pack和Framework版本可能会放松限制。 以下查询运算符阻止查询被并行化,除非 源元素处于其原始索引位置:
大多数查询运算符都会更改元素的索引位置(包括那些元素的索引位置) 删除元素,例如Where)。这意味着如果你想使用前面的 运营商,他们通常需要在查询的开头
因此,实际上,使用TakeWhile可以防止.AsParallel()并行化。很难说为什么哪里杀了订阅,但把它放在AsParallel 之前可能会解决问题。