【发布时间】:2010-02-18 10:51:48
【问题描述】:
我有一个要并行处理的可观察集合,然后在过滤时观察处理后的值,最后订阅一个接收过滤值的处理程序。
我的示例在语法上是正确的并且编译得很好,当我运行代码时,会评估执行过滤的 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 不会?
如果它很重要,我会在 Visual Studio 2010 RC 中使用面向 .Net 4.0 Framework Client Profile 的项目开发我的代码。
更新:基于@Sergeys answer,我重新设计了Where 过滤器的位置。以下代码按预期工作:
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 );
仍然感觉有点尴尬,必须先将初始 observable processedStrings 转换为 enumerable 以使其并行化,然后再将其转换回 observable 以订阅最终结果。
【问题讨论】:
-
另一个修复可能是在 .ToObservable 之前使用 .AsOrdered - 没有尝试过。
-
.AsParallel().Where(...).AsOrdered().ToObservable()... 失败:
System.InvalidOperationException: AsOrdered may only be called on the result of AsParallel, ParallelEnumerable.Range, or ParallelEnumerable.Repeat.似乎是为了过滤 after并行化,唯一的选择是使用 .TakeWhile。
标签: c# system.reactive parallel-extensions