作为我们应用程序的一部分(现在生产大约4个月),我们有一个来自外部设备的数据流,我们将其转换为IObservable
到目前为止,我们一直在使用以下方法来生成它,并且它一直运行良好。
IObservable<string> ObserveStringStream(Stream inputStream)
{
var streamReader = new StreamReader(inputStream);
return Observable
.Create<string>(observer => Scheduler.ThreadPool
.Schedule(() => ReadLoop(streamReader, observer)));
}
private void ReadLoop(StreamReader reader, IObserver<string> observer)
{
while (true)
{
try
{
var line = reader.ReadLine();
if (line != null)
{
observer.OnNext(line);
}
else
{
observer.OnCompleted();
break;
}
}
catch (Exception ex)
{
observer.OnError(ex);
break;
}
}
}
昨晚我想知道是否有办法使用yield return
语法来实现相同的结果并想出了这个:
IObservable<string> ObserveStringStream(Stream inputStream)
{
var streamReader = new StreamReader(inputStream);
return ReadLoop(streamReader)
.ToObservable(Scheduler.ThreadPool);
}
private IEnumerable<string> ReadLoop(StreamReader reader)
{
while (true)
{
var line = reader.ReadLine();
if (line != null)
{
yield return line;
}
else
{
yield break;
}
}
}
它似乎工作得很好而且更干净,但我想知道是否有任何优点或缺点,而不是另一种方式,或者是否有更好的方式。
答案 0 :(得分:13)
我认为你在那里有个好主意(将Stream
变成Enumerable<string>
然后IObservable<string>
)。但是, IEnumerable 代码可以更清晰:
IEnumerable<string> ReadLines(Stream stream)
{
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
yield return reader.ReadLine();
}
}
然后是 IObservable :
IObservable<string> ObserveLines(Stream inputStream)
{
return ReadLines(inputStream).ToObservable(Scheduler.ThreadPool);
}
这更短,更易读,并且正确处理流。它也很懒惰。
ToObservable
扩展程序负责捕获OnNext
个事件(新行)以及OnCompleted
事件(可枚举结束)和OnError
。
答案 1 :(得分:2)
我没有代码可以提供,但是这里是如何做到异步预异步CTP。
[对于脱脂阅读器的注意事项:如果你不需要扩展太多就不需要打扰]
创建一个AsyncTextReader实现,它本身就是Observable。 ctor接收一个Stream,并在流上执行BeginRead(256bytes),将自身作为延续,然后返回。
进入延续时,调用EndRead,并将返回的字节添加到类的一个小缓冲区中。重复此操作直到缓冲区包含一个或多个行结束序列(根据TextWriter)。发生这种情况时,通过Observable接口将缓冲区的那些位作为字符串发送出来,然后重复。
完成后,发出OnComplete等信号......(并处理流)。如果从延续中的EndReadByte中抛出异常,请捕获它并将其传递给OnError接口。
调用代码然后看起来像:
IObservable = new AsyncTextReader(stream);
这很好。只需要确保你不要对缓冲区处理做任何麻烦。
伪代码:
public ctor(Stream stream){
this._stream = stream;
BeginRead();
return;
}
private void BeginRead(){
// kick of async read and return (synchronously)
this._stream.BeginRead(_buffer,0,256,EndRead,this);
}
private void EndRead(IAsyncResult result){
try{
// bytesRead will be *up to* 256
var bytesRead = this._stream.EndRead(result);
if(bytesRead < 1){
OnCompleted();
return;
}
// do work with _buffer, _listOfBuffers
// to get lines out etc...
OnNext(aLineIFound); // times n
BeginRead(); // go round again
}catch(Exception err){
OnException(err);
}
}
好的,这是APM,只有母亲才能爱。我非常期待等待替代方案。
ps:读者是否应关闭流是一个有趣的问题。我说不,因为它没有创造它。
答案 2 :(得分:0)
使用async / await支持,以下内容最有可能是您最好的选择:
IObservable<string> ObserveStringStream(Stream inputStream)
{
return Observable.Using(() => new StreamReader(inputStream),
sr => Observable.Create<string>(async (obs, ct) =>
{
while (true)
{
ct.ThrowIfCancellationRequested();
var line = await sr.ReadLineAsync().ConfigureAwait(false);
if (line == null)
break;
obs.OnNext(line);
}
obs.OnCompleted();
}));
}