我想读取一个流并缓冲它的输出,以便消费者可以在生产者完成对流的完整读取之前读取它。 例如,从Http流中读取并转发到另一个... 这使用.net4.5 TPL库。 我发现Nito AsyncCollection及以下是我写的。但是我想知道这是否正确,因为当我调试时,即使使用长字符串来测试它,甚至在从asp.net vnext管道调出时,它也会按顺序读取和写入...我怎么能确定它在非调试模式下具有正确的行为?
using Nito.AsyncEx;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
namespace Absyla.Core
{
public class PipeStream
{
private AsyncCollection<byte[]> _chunks = new AsyncCollection<byte[]>(1);
public async Task ReadFrom(Stream s)
{
try
{
byte[] buffer;
int bytesRead = 0;
int totalBytesRead = 0;
do
{
long buffsize = Math.Min(s.Length - totalBytesRead, 1L);
buffer = new byte[buffsize];
bytesRead = await s.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead > 0)
{
int readId = System.Environment.CurrentManagedThreadId;
await _chunks.AddAsync(buffer);
totalBytesRead += bytesRead;
}
}
while (bytesRead > 0);
}
finally
{
_chunks.CompleteAdding();
}
}
public async Task WriteTo(Stream s)
{
while (await _chunks.OutputAvailableAsync())
{
int writeId = System.Environment.CurrentManagedThreadId;
byte[] buffer = await _chunks.TakeAsync();
await s.WriteAsync(buffer, 0, buffer.Length);
}
}
}
}
这是一个测试
[Fact]
public async Task ShouldBufferLittleString()
{
String testString = "test in";
PipeStream ps = new PipeStream();
MemoryStream ms2 = new MemoryStream();
await ps.ReadFrom(testString.AsStream());
await ps.WriteTo(ms2);
string result = ms2.ReadFromStart();
result.ShouldBe(testString);
}
注意:AsStream()方法是一个从String返回流对象的扩展。
当我发现它很简单时,我想知道这是否真的是正确的,如果它真的能完成我想要的工作......
更新:更改以下内容:
private AsyncCollection<byte[]> _chunks = new AsyncCollection<byte[]>();
到
private AsyncCollection<byte[]> _chunks = new AsyncCollection<byte[]>(2);
进行了限制工作。但是我的测试块。当我这样做时它确实有效:
public async Task ShouldBufferLittleString()
{
String testString = "test in";
PipeStream ps = new PipeStream();
MemoryStream ms2 = new MemoryStream();
var readTask = ps.ReadFrom(testString.AsStream());
var writeTask = ps.WriteTo(ms2);
await Task.WhenAll(readTask, writeTask);
string result = ms2.ReadFromStart();
result.ShouldBe(testString);
}
但在我的代码中,我不能总是引用读写任务,因为它可以从代码中的不同位置调用。
我尝试更改为BufferBlock,但遇到了同样的问题。
更新2: 我稍微更改了管道流代码以添加throtling和1缓冲区以供测试。
在测试中,当我等待它立即阻塞时,但是当我创建任务并等待它们之后,测试通过。
// this blocks:
await ps.ReadFrom(testString.AsStream());
await ps.WriteTo(ms2);
//this passes
var read = ps.ReadFrom(testString.AsStream());
var write = ps.WriteTo(ms2);
await read;
await write;