有人可以解释为什么即使到达流的末尾,以下AsObservable
方法也会创建无限循环吗?
public static class StreamExt {
public static IObservable<byte> AsObservable(this Stream stream, int bufferSize) {
return Observable
.FromAsync(cancel => stream.ReadBytes(bufferSize, cancel))
.Repeat()
.TakeWhile(bytes => bytes != null) // EndOfStream
.SelectMany(bytes => bytes);
}
private static async Task<byte[]> ReadBytes(this Stream stream, int bufferSize, CancellationToken cancel) {
var buf = new byte[bufferSize];
var bytesRead = await stream
.ReadAsync(buf, 0, bufferSize, cancel)
.ConfigureAwait(false);
if (bytesRead < 1) return null; // EndOfStream
var result_size = Math.Min(bytesRead, bufferSize);
Array.Resize(ref buf, result_size);
return buf;
}
}
快速测试显示它产生无限循环:
class Program {
static void Main(string[] args) {
using (var stream = new MemoryStream(new byte[] { 1, 2, 3 })) {
var testResult = stream
.AsObservable(1024)
.ToEnumerable()
.ToArray();
Console.WriteLine(testResult.Length);
}
}
}
当然我可以添加.SubscribeOn(TaskPoolScheduler.Default)
但是,无限循环保持活动状态(阻止任务池调度程序+从Stream
无限读取)。
[更新2017-05-09]
Shlomo发布了一个更好的例子来重现这个问题:
int i = 0;
var testResult = Observable.FromAsync(() => Task.FromResult(i++))
.Repeat()
.TakeWhile(l => l < 3);
testResult.Subscribe(b => Console.WriteLine(b), e => { }, () => Console.WriteLine("OnCompleted"));
Console.WriteLine("This is never printed.");
答案 0 :(得分:1)
您可以使用以下方式确认正确生成OnCompleted
:
using (var stream = new MemoryStream(new byte[] { 1, 2, 3 }))
{
var testResult = stream
.AsObservable(1024)
;
testResult.Subscribe(b => Console.WriteLine(b), e => {}, () => Console.WriteLine("OnCompleted"));
}
看起来.FromAsync
+ .Repeat
组合存在问题。以下代码的行为类似:
int i = 0;
var testResult = Observable.FromAsync(() => Task.FromResult(i++))
.Repeat()
.TakeWhile(l => l < 3)
;
testResult.Subscribe(b => Console.WriteLine(b), e => { }, () => Console.WriteLine("OnCompleted"));
Console.WriteLine("This is never printed.");
...而这段代码正确终止:
var testResult = Observable.Generate(0, i => true, i => i + 1, i => i)
.Repeat()
.TakeWhile(l => l < 3)
;
testResult.Subscribe(b => Console.WriteLine(b), e => { }, () => Console.WriteLine("OnCompleted"));
Console.WriteLine("This is printed.");
答案 1 :(得分:1)
对于那些最终需要答案的人,不仅仅是一个解释:问题似乎是FromAsync
的默认调度程序,如this self-answered question所示。如果您调整为“当前线程”,则调度程序Repeat().TakeWhile(...)
的行为将更加可预测。例如。 (摘录自问题):
.FromAsync(cancel => stream.ReadBytes(bufferSize, cancel),
System.Reactive.Concurrency.Scheduler.CurrentThread)
.Repeat()
.TakeWhile(bytes => bytes != null) // EndOfStream
答案 2 :(得分:0)
Shane Neuville发布了包含此行为解释的链接: