使用.net

时间:2015-04-22 13:44:03

标签: c# .net async-await task-parallel-library tpl-dataflow

我想读取一个流并缓冲它的输出,以便消费者可以在生产者完成对流的完整读取之前读取它。 例如,从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;

0 个答案:

没有答案