一尘不染

.NET异步流读取/写入

c#

我一直在尝试解决此“并行编程”考试练习(在C#中):

知道Stream该类包含int Read(byte[] buffer, int offset, int size)void Write(byte[] buffer, int offset, int size)方法,就可以在C#中实现NetToFile将从NetworkStream net实例接收的所有数据复制到FileStream file实例的方法。要进行传输,请使用异步读取和同步写入,以避免在读取操作期间阻塞一个线程。当net读取操作返回值0
时,传输结束。为简化起见,不必支持操作的受控取消。

void NetToFile(NetworkStream net, FileStream file);

我一直在尝试解决此问题,但我一直在努力解决与问题本身相关的问题。但是首先,这是我的代码:

public static void NetToFile(NetworkStream net, FileStream file) {
    byte[] buffer = new byte[4096]; // buffer with 4 kB dimension
    int offset = 0; // read/write offset
    int nBytesRead = 0; // number of bytes read on each cycle

    IAsyncResult ar;
    do {
        // read partial content of net (asynchronously)
        ar = net.BeginRead(buffer,offset,buffer.Length,null,null);
        // wait until read is completed
        ar.AsyncWaitHandle.WaitOne();
        // get number of bytes read on each cycle
        nBytesRead = net.EndRead(ar);

        // write partial content to file (synchronously)
        fs.Write(buffer,offset,nBytesRead);
        // update offset
        offset += nBytesRead;
    }
    while( nBytesRead > 0);
}

我的问题是,在问题陈述中说:

要进行传输,请使用异步读取和同步写入,避免在读取操作期间阻塞一个线程

我不确定我的解决方案是否能够完成本练习中需要的功能,因为我一直AsyncWaitHandle.WaitOne()在等异步读取完成。

另一方面,在这种情况下,我并没有真正弄清楚什么是“非阻塞”解决方案,因为FileStream写入是要同步进行的……为了做到这一点,我必须等到NetworkStream阅读完成后继续FileStream写作,不是吗?

您能帮我这个忙吗?


[编辑1] 使用 回调 解决方案

好的,如果我了解Mitchel Sellerswillvv的回答,则建议我使用回调方法将其转换为“非阻塞”解决方案。这是我的代码,然后:

byte[] buffer; // buffer

public static void NetToFile(NetworkStream net, FileStream file) {
    // buffer with same dimension as file stream data
    buffer = new byte[file.Length];
    //start asynchronous read
    net.BeginRead(buffer,0,buffer.Length,OnEndRead,net);
}

//asynchronous callback
static void OnEndRead(IAsyncResult ar) {
    //NetworkStream retrieve
    NetworkStream net = (NetworkStream) ar.IAsyncState;
    //get number of bytes read
    int nBytesRead = net.EndRead(ar);

    //write content to file
    //... and now, how do I write to FileStream instance without
    //having its reference??
    //fs.Write(buffer,0,nBytesRead);
}

您可能已经注意到,由于无法引用FileStream要调用“ Write(…)”方法的实例,因此我陷入了回调方法。

此外,这不是线程安全的解决方案,因为该byte[]字段是公开的,并且可以在并发NetToFile调用之间共享。我不知道如何解决这个问题而不将其暴露byte[]在外层视野中……我几乎可以肯定它可能不会以这种方式暴露出来。

我不想使用lambda或匿名方法解决方案,因为这不在“并发编程”课程的课程中。


阅读 326

收藏
2020-05-19

共1个答案

一尘不染

您将需要使用NetStream读取的回调来处理此问题。坦率地说,将复制逻辑包装到其自己的类中可能会更容易,以便您可以维护活动Streams的实例。

这是我的处理方式(未经测试):

public class Assignment1
{
    public static void NetToFile(NetworkStream net, FileStream file) 
    {
        var copier = new AsyncStreamCopier(net, file);
        copier.Start();
    }

    public static void NetToFile_Option2(NetworkStream net, FileStream file) 
    {
        var completedEvent = new ManualResetEvent(false);

        // copy as usual but listen for completion
        var copier = new AsyncStreamCopier(net, file);
        copier.Completed += (s, e) => completedEvent.Set();
        copier.Start();

        completedEvent.WaitOne();
    }

    /// <summary>
    /// The Async Copier class reads the input Stream Async and writes Synchronously
    /// </summary>
    public class AsyncStreamCopier
    {
        public event EventHandler Completed;

        private readonly Stream input;
        private readonly Stream output;

        private byte[] buffer = new byte[4096];

        public AsyncStreamCopier(Stream input, Stream output)
        {
            this.input = input;
            this.output = output;
        }

        public void Start()
        {
            GetNextChunk();
        }

        private void GetNextChunk()
        {
            input.BeginRead(buffer, 0, buffer.Length, InputReadComplete, null);
        }

        private void InputReadComplete(IAsyncResult ar)
        {
            // input read asynchronously completed
            int bytesRead = input.EndRead(ar);

            if (bytesRead == 0)
            {
                RaiseCompleted();
                return;
            }

            // write synchronously
            output.Write(buffer, 0, bytesRead);

            // get next
            GetNextChunk();
        }

        private void RaiseCompleted()
        {
            if (Completed != null)
            {
                Completed(this, EventArgs.Empty);
            }
        }
    }
}
2020-05-19