.NET异步流读取/写NET

2023-09-02 01:24:22 作者:残风葬

我一直在试图解决这个并发编程考试练习(在C#):​​

  

明知类包含 INT读(byte []的缓冲区,诠释抵消,诠释大小)无效写入(byte []的缓冲区,诠释抵消,诠释大小)的方法,实现在C#中的 NetToFile 方法,它会将所有从的NetworkStream接收到的数据网实例给的FileStream文件实例。要做好转移,使用异步读取和同步写入,避免了一个线程在读操作被阻塞。传送结束时,读操作返回值0。为了简化,这是没有必要的支持控制的取消操作。

 无效NetToFile(的NetworkStream网络文件流文件);
 

我一直在试图解决这个练习,但我挣扎与问题本身有关的问题。但首先,这里是我的code:

 公共静态无效NetToFile(的NetworkStream网络文件流文件){
    byte []的缓冲区=新的字节[4096]; //缓冲区4 KB尺寸
    INT偏移= 0; //读/写偏移
    INT nBytesRead = 0; //读取在每个周期的字节数

    IAsyncResult的AR;
    做 {
        //读取扣除部分内容(异步)
        芳= net.BeginRead(缓冲,抵消,buffer.Length,NULL,NULL);
        //等到读完成
        ar.AsyncWaitHandle.WaitOne();
        //获取阅读在每个周期的字节数
        nBytesRead = net.EndRead(AR);

        //写部分内容到文件(同步)
        fs.Write(缓冲,抵消,nBytesRead);
        //更新偏移
        胶印+ = nBytesRead;
    }
    而(nBytesRead大于0);
}
 

我的问题是,在这个问题发言,说:

  无法从传输连接中读取数据 net io connectionclosed 实例讲解西门子300和200PLC通讯,通讯可能是PLC中最难应用的... weixin 39988888的博客 CSDN博客

要做好转移,使用异步   读取和写入同步,避免   一个线程在读取被阻塞   操作

我真的不知道,如果我的解决方案完成什么都想在这个练习,因为我使用 AsyncWaitHandle.WaitOne()等到异步读取完成

在另一边,我没有真正搞清楚什么,就是要在这种情况下一个无阻塞的解决方案,因为的FileStream 写的意思要进行同步......要做到这一点,我要等到的NetworkStream 读完成后继续执行的FileStream 写作,不是吗?

你能,请帮助我吗?预先感谢您的colaboration。

使用的回调的解决方案

好吧,如果我明白米切尔卖家和的 willvv 回复(谢谢你们),我一直在劝使用回调方法把它变成一个无阻塞的解决方案。这是我的code,然后...

 字节[]缓冲区; // 缓冲

公共静态无效NetToFile(的NetworkStream网络文件流文件){
    //缓冲区尺寸相同的文件流数据
    缓冲区=新的字节[file.Length]
    //启动异步读
    net.BeginRead(缓冲,0,buffer.Length,OnEndRead,净);
}

//异步回调
静态无效OnEndRead(IAsyncResult的AR){
    //的NetworkStream检索
    的NetworkStream净值=(的NetworkStream)ar.IAsyncState;
    //获取字节数读
    INT nBytesRead = net.EndRead(AR);

    //内容写入文件
    // ......现在,我怎么写的FileStream实例,不需要
    //有它的参考?
    //fs.Write(buffer,0,nBytesRead);
}
 

正如你可能已经注意到,我卡上的回调方法,因为我没有一个参照的FileStream 实例,我想援引写(...)方法。

此外,这不是一个线程安全的解决方案,因为字节[] 字段进行曝光,可之间并发共享 NetToFile 调用。我不知道如何解决这个问题,而无需在外部范围的公开此字节[] 字段......和我几乎可以肯定它可能不会被曝光了这款方法。

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

解决方案

您将需要使用来自NetStream的回调读来处理这个问题。坦率地说,它可能更容易缠绕复制逻辑到它自己的类,这样就可以保持活跃流的实例。

这是我会怎么对待它(未测试):

 公共类分配1
{
    公共静态无效NetToFile(的NetworkStream网络文件流文件)
    {
        VAR复印机=新AsyncStreamCopier(净值,档案);
        copier.Start();
    }

    公共静态无效NetToFile_Option2(的NetworkStream网络文件流文件)
    {
        VAR completedEvent =新的ManualResetEvent(假);

        //复制像往常一样,但听竣工
        VAR复印机=新AsyncStreamCopier(净值,档案);
        copier.Completed + =(S,E)=> completedEvent.Set();
        copier.Start();

        completedEvent.WaitOne();
    }

    ///<总结>
    ///的异步复印机类读取输入流异步,同步写入
    ///< /总结>
    公共类AsyncStreamCopier
    {
        公共事件的EventHandler完成;

        私人只读流输入;
        私人只读流输出;

        私人byte []的缓冲区=新的字节[4096];

        公共AsyncStreamCopier(流输入,流输出)
        {
            this.input =输入;
            this.output =输出;
        }

        公共无效启动()
        {
            GetNextChunk();
        }

        私人无效GetNextChunk()
        {
            input.BeginRead(缓冲液,0,buffer.Length,InputReadComplete,空);
        }

        私人无效InputReadComplete(IAsyncResult的AR)
        {
            //输入读取异步完成
            INT读取动作= input.EndRead(AR);

            如果(读取动作== 0)
            {
                RaiseCompleted();
                返回;
            }

            //写同步
            output.Write(缓冲液,0,读取动作);

            //获得下一个
            GetNextChunk();
        }

        私人无效RaiseCompleted()
        {
            如果(完成!= NULL)
            {
                完成(这一点,EventArgs.Empty);
            }
        }
    }
}
 

I have been trying to solve this "Concurrent Programming" exam exercise (in C#):

Knowing that Stream class contains int Read(byte[] buffer, int offset, int size) and void Write(byte[] buffer, int offset, int size) methods, implement in C# the NetToFile method that copies all data received from NetworkStream net instance to the FileStream file instance. To do the transfer, use asynchronous reads and synchronous writes, avoiding one thread to be blocked during read operations. The transfer ends when the net read operation returns value 0. To simplify, it is not necessary to support controlled cancel of the operation.

void NetToFile(NetworkStream net, FileStream file);

I've been trying to solve this exercise, but I'm struggling with a question related with the question itself. But first, here is my code:

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

The question I have is that, in the question statement, is said:

To do the transfer, use asynchronous reads and synchronous writes, avoiding one thread to be blocked during read operations

I'm not really sure if my solution accomplishes what is wanted in this exercise, because I'm using AsyncWaitHandle.WaitOne() to wait until the asynchronous read completes.

On the other side, I'm not really figuring out what is meant to be a "non-blocking" solution in this scenario, as the FileStream write is meant to be made synchronously... and to do that, I have to wait until NetworkStream read completes to proceed with the FileStream writing, isn't it?

Can you, please, help me out with this? Thanks in advance for your colaboration.

[ EDIT 1 ] Using callback solution

Ok, if I understood what Mitchel Sellers and willvv replied (thank you guys), I've been counseled to use a callback method to turn this into a "non-blocking" solution. Here is my code, then...

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

As you may have noticed, I'm stuck on the callback method, as I don't have a reference to the FileStream instance where I want to invoke the "Write(...)" method.

Additionally, this is not a thread-safe solution, as the byte[] field is exposed and may be shared among concurrent NetToFile invocations. I don't know how to solve this problem without exposing this byte[] field in the outer-scope... and I'm almost sure it may not be exposed this way.

I don't want to use a lambda or anonymous method solution, because that's not in the curriculum of "Concurrent Programing" course.

解决方案

You are going to need to use the callback from the NetStream read to handle this. And frankly it might be easier to wrap the copying logic into its own class so that you can maintain the instance of the active Streams.

This is how I'd approach it (not tested):

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