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

时间:2009-01-20 19:22:29

标签: c# asynchronous stream serial-port operation

我试图使用.NET2.0 SerialPort的.BaseStream属性来进行异步读写(BeginWrite / EndWrite,BeginRead / EndRead)。

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

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

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

最初的CSwitch Delta(再次来自Process Explorer)大约是2500,无论如何这似乎都很高。每次出现新线程时,此值都会增加,CPU负载也会相应增加。

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

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

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

感激地收到任何评论。

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

4 个答案:

答案 0 :(得分:5)

一些建议:

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

也不要一直创建新的AsyncCallback。创建一个Read和一个Write AsyncCallback,并在每次开始调用时使用它。

答案 1 :(得分:4)

完全不需要BeginWrite。你只发送3个字节,它们很容易放入发送缓冲区,当你发送下一组时,你总是确定缓冲区是空的。

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

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

答案 2 :(得分:0)

是否可以从串口获取数据并直接将其发送到文件?在高波特率(1兆比特)下,很难处理这一数量的不间断数据。

答案 3 :(得分:0)

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