【问题标题】:How to correctly use .NET2.0 serial port .BaseStream for async operation如何正确使用.NET2.0串口.BaseStream进行异步操作
【发布时间】:2010-10-02 12:56:08
【问题描述】:

我正在尝试使用 .NET2.0 SerialPort 的 .BaseStream 属性进行异步读写(BeginWrite/EndWrite、BeginRead/EndRead)。

我在这方面取得了一些成功,但一段时间后,我注意到(使用 Process Explorer)应用正在使用的句柄逐渐增加,并且偶尔会增加一个额外的线程,这也增加了句柄计数。

每次出现新线程时,上下文切换率也会增加。

应用程序不断向 PLC 设备发送 3 个字节,并以 57600 的波特率返回 800 个左右的字节。

最初的 CSwitch Delta(同样来自 Process Explorer)大约是 2500,无论如何这似乎非常高。每出现一个新线程,这个值就会增加,CPU负载也会相应增加。

我希望有人可能做过类似的事情,并且可以帮助我,甚至说'以上帝的名义,不要那样做。'

在下面的代码中,'this._stream' 是从 SerialPort.BaseStream 获得的,而 CommsResponse 是我用作 IAsyncresult 状态对象的类。

此代码对于我作为使用串行端口的替代方法创建的 TCP 连接是通用的,(我有一个 CommsChannel 基类,具有从它派生的串行和 TCP 通道)并且它没有这些问题,所以我我有理由相信 CommsResponse 类没有任何问题。

感谢收到任何 cmets。

    /// <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.
        }
    }

【问题讨论】:

    标签: c# asynchronous stream serial-port operation


    【解决方案1】:

    一些建议:

    由于您只发送 3 个字节,因此您可以进行同步写入操作。延迟不是什么大问题。

    也不要一直创建新的 AsyncCallback。创建一个 Read 和一个 Write AsyncCallback 并在每个 begin 调用中使用它。

    【讨论】:

    • 感谢您的回复。关于创建回调的好建议。我已经尝试过了,但是句柄/线程/上下文切换率仍然在增加,尽管它似乎确实以较慢的速度增加,所以是一种改进。
    【解决方案2】:

    根本不需要 BeginWrite。您只发送 3 个字节,它们很容易放入传输缓冲区,并且当您发送下一组时,您始终可以确定缓冲区是空的。

    请记住,串行端口比 TCP/IP 连接慢得多。您很可能最终会为收到的每个字节调用 BeginRead()。这给了线程池一个很好的锻炼,你肯定会看到很多上下文切换。不太确定手柄消耗。请务必在没有附加调试器的情况下对其进行测试。

    尝试使用 DataReceived 而不是 BeginRead() 绝对是您应该尝试的。拉而不是推,当发生某些事情时,您将使用线程池线程,而不是总是有一个处于活动状态。

    【讨论】:

    • 我同意 BeginWrite 的观点,这不是真的必要。 BeginRead 不会针对每个字节触发,但它确实会非常频繁地触发。也许我可以使用 DataReceived 并将字节传递到我自己的流中以保持类一致?顺便说一句,我也在测试“发布”版本。
    • @Hans:正如 Andy 所说,BeginRead/EndRead 可以轻松处理每次调用的多个字节(取决于超时设置)。并且 S.IO.P.SerialPort 始终阻塞线程池线程以检测数据并触发 DataReceived,因此您可以想象较低的资源使用率。实际上,BeginRead/EndRead 所需的内核调用比检测活动然后读取解决方案要少。
    【解决方案3】:

    是否可以将串口传入的数据直接发送到文件中?在高波特率(1 兆波特)下,很难处理这么多的不间断数据。

    【讨论】:

      【解决方案4】:

      设备的响应是否总是固定大小?如果是这样,请尝试使用SerialPort.Read 并传递数据包大小。这将阻塞,因此将其与DataReceived 结合使用。更好的是,如果响应总是以相同的字符结束,并且这个结束签名保证在数据包中是唯一的,那么设置NewLine 属性并使用ReadLine。这将使您免受未来数据包大小变化的影响。

      【讨论】:

      • 不幸的是,数据包的大小是可变的。
      • 无赖。那么你怎么知道你什么时候收到了一个完整的数据包呢?
      • 数据包包含头和终止字节,数据包长度和校验和在其中编码。
      • 如果保证payload中不使用终结符,可以设置NewLine,使用ReadLine
      • 不幸的是(再次!)数据包包含二进制字节数据,而不是 ASCII,因此从 0x0 到 0xFF 的所有值都是有效的。不过还是感谢您的建议。
      猜你喜欢
      • 1970-01-01
      • 2017-09-09
      • 2011-12-09
      • 1970-01-01
      • 1970-01-01
      • 2016-12-20
      • 2016-02-09
      • 2018-01-20
      • 1970-01-01
      相关资源
      最近更新 更多