简体   繁体   English

如何正确使用.NET2.0串口.BaseStream进行异步操作

[英]How to correctly use .NET2.0 serial port .BaseStream for async operation

I am attempting to use the .BaseStream property of the .NET2.0 SerialPort to do asynchronous reads and writes (BeginWrite/EndWrite, BeginRead/EndRead). 我试图使用.NET2.0 SerialPort的.BaseStream属性来进行异步读写(BeginWrite / EndWrite,BeginRead / EndRead)。

I am having some success in this, but after a time, I notice (using Process Explorer) a very gradual increase in the Handles the app is using, and occasionally an extra thread, which also increases the Handle count. 我在这方面取得了一些成功,但过了一段时间后,我注意到(使用Process Explorer)应用程序正在使用的句柄逐渐增加,偶尔会增加一个额外的线程,这也增加了句柄数。

The context switch rate also increases each time a new thread appears. 每次出现新线程时,上下文切换速率也会增加。

The app constantly sends 3 bytes to a PLC device, and gets 800 or so bytes in return, and does so at a baud rate of 57600. 应用程序不断向PLC设备发送3个字节,并返回800个左右的字节,并以波特率57600进行。

The initial CSwitch Delta (again, from Process Explorer) is around 2500, which seems very high anyway. 最初的CSwitch Delta(再次来自Process Explorer)大约是2500,无论如何它似乎都很高。 Each time a new thread appears, this value increases, and the CPU load increases accordingly. 每次出现新线程时,此值都会增加,CPU负载也会相应增加。

I'm hoping that somebody might have done something similar, and can help me out, or even say 'In God's name, don't do it that way.' 我希望有人可能做过类似的事情,可以帮助我,甚至说'以上帝的名义,不要这样做。'

In the code below, 'this._stream' is obtained from SerialPort.BaseStream, and CommsResponse is a class I use as the IAsyncresult state object. 在下面的代码中,'this._stream'是从SerialPort.BaseStream获得的,而CommsResponse是我用作IAsyncresult状态对象的类。

This code is common to a TCP connection I make as an alternative to using the serial port, (I have a CommsChannel base class, with a serial and TCP channel derived from it) and it has none of these problems so I'm reasonably hopeful that there is nothing wrong with the CommsResponse class. 这个代码对于我作为替代使用串口的TCP连接是常见的(我有一个CommsChannel基类,从它派生的串行和TCP通道)并且它没有这些问题所以我有理由充满希望CommsResponse类没有任何问题。

Any comments gratefully received. 感激地收到任何评论。

    /// <summary>
    /// Write byte data to the channel.
    /// </summary>
    /// <param name="bytes">The byte array to write.</param>
    private void Write(byte[] bytes)
    {
        try
        {
            // Write the data to the port asynchronously.
            this._stream.BeginWrite(bytes, 0, bytes.Length, new AsyncCallback(this.WriteCallback), null);
        }
        catch (IOException ex)
        {
            // Do stuff.
        }
        catch (ObjectDisposedException ex)
        {
            // Do stuff.
        }
    }

    /// <summary>
    /// Asynchronous write callback operation.
    /// </summary>
    private void WriteCallback(IAsyncResult ar)
    {
        bool writeSuccess = false;

        try
        {
            this._stream.EndWrite(ar);
            writeSuccess = true;
        }
        catch (IOException ex)
        {
            // Do stuff.
        }

        // If the write operation completed sucessfully, start the read process.
        if (writeSuccess) { this.Read(); }
    }

    /// <summary>
    /// Read byte data from the channel.
    /// </summary>
    private void Read()
    {
        try
        {
            // Create new comms response state object.
            CommsResponse response = new CommsResponse();

            // Begin the asynchronous read process to get response.
            this._stream.BeginRead(this._readBuffer, 0, this._readBuffer.Length, new AsyncCallback(this.ReadCallback), response);
        }
        catch (IOException ex)
        {
            // Do stuff.
        }
        catch (ObjectDisposedException ex)
        {
            // Do stuff.
        }
    }

    /// <summary>
    /// Asynchronous read callback operation.
    /// </summary>
    private void ReadCallback(IAsyncResult ar)
    {
        // Retrieve the comms response object.
        CommsResponse response = (CommsResponse)ar.AsyncState;

        try
        {
            // Call EndRead to complete call made by BeginRead.
            // At this point, new data will be in this._readbuffer.
            int numBytesRead = this._stream.EndRead(ar);

            if (numBytesRead > 0)
            {
                // Create byte array to hold newly received bytes.
                byte[] rcvdBytes = new byte[numBytesRead];

                // Copy received bytes from read buffer to temp byte array
                Buffer.BlockCopy(this._readBuffer, 0, rcvdBytes, 0, numBytesRead);

                // Append received bytes to the response data byte list.
                response.AppendBytes(rcvdBytes);

                // Check received bytes for a correct response.
                CheckResult result = response.CheckBytes();

                switch (result)
                {
                    case CheckResult.Incomplete: // Correct response not yet received.
                        if (!this._cancelComm)
                        {
                            this._stream.BeginRead(this._readBuffer, 0, this._readBuffer.Length,
                                new AsyncCallback(this.ReadCallback), response);
                        }
                        break;

                    case CheckResult.Correct:  // Raise event if complete response received.
                        this.OnCommResponseEvent(response);
                        break;

                    case CheckResult.Invalid: // Incorrect response
                        // Do stuff.
                        break;

                    default: // Unknown response
                        // Do stuff.
                        break;
                }
            }
            else
            {
                // Do stuff.
            }
        }
        catch (IOException ex)
        {
            // Do stuff.
        }
        catch (ObjectDisposedException ex)
        {
            // Do stuff.
        }
    }

Some suggestions: 一些建议:

Since you are only sending 3 bytes, you could have a synchronous Write Operation. 由于您只发送3个字节,因此可以进行同步写操作。 The delay wouldn't be much of an issue. 延迟不会是一个问题。

Also don't create a new AsyncCallback all the time. 也不要一直创建新的AsyncCallback。 Create one Read and one Write AsyncCallback and use that in every begin call. 创建一个Read和一个Write AsyncCallback,并在每次开始调用时使用它。

No need at all for BeginWrite. 完全不需要BeginWrite。 You only send 3 bytes, they'll easily fit in the transmit buffer, and you're always sure that the buffer is empty when you send the next set. 您只发送3个字节,它们很容易适合发送缓冲区,并且当您发送下一个集合时,您始终确定缓冲区为空。

Keep in mind that serial ports are much slower than TCP/IP connections. 请记住,串行端口比TCP / IP连接慢得多。 It is pretty likely that you end up calling BeginRead() for every single byte that you receive. 您最终可能会为您收到的每个字节调用BeginRead()。 That gives the thread pool a good workout, you'd definitely see a lot of context switches. 这给了线程池一个很好的锻炼,你肯定会看到很多上下文切换。 Not so sure about handle consumption. 手柄消费不太确定。 Be sure to test that without the debugger attached. 请确保在未连接调试器的情况下进行测试。

Trying DataReceived instead of BeginRead() is definitely something you should try. 尝试DataReceived而不是BeginRead()肯定是你应该尝试的。 Pull instead of push, you'll use a threadpool thread when there's something happening instead of always having one active. 拉动而不是推动,当发生某些事情而不是总是有一个活动时,你将使用线程池线程。

Is the response from the device always a fixed size? 设备的响应总是固定大小吗? If so, try using SerialPort.Read and pass the packet size. 如果是这样,请尝试使用SerialPort.Read并传递数据包大小。 This will block, so combine it with DataReceived . 这将阻止,因此将其与DataReceived结合使用。 Better yet, if the response always ends with the same character(s), and this ending signature is guaranteed to be unique in the packet, set the NewLine property and use ReadLine . 更好的是,如果响应始终以相同的字符结束,并且此结束签名在数据包中保证是唯一的,请设置NewLine属性并使用ReadLine This will immunize you against future packet size changes. 这将使您免受未来数据包大小更改的影响。

Is it possible to take the data comming in from the serial port and directly send it to a file? 是否可以从串口接收数据并直接将其发送到文件? At high a baud rates (1 MegaBaud) it is difficult to handle this amount of non-stop data. 在高波特率(1兆波特)下,很难处理这么多的不间断数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM