我一直在尝试解决此"并发编程"考试练习(在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]Using callback solution

好吧,如果我明白的话米切尔卖家Willvv回答说,我得到了建议,使用回调方法将其转变为"非阻滞"解决方案。这是我的代码,然后:

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我想调用"写(…)“方法的实例。

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

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

答案

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

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

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);
            }
        }
    }
}

来自: stackoverflow.com