15

I am attempting to use the .BaseStream property of the .NET2.0 SerialPort to do asynchronous reads and writes (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.

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.

The initial CSwitch Delta (again, from Process Explorer) is around 2500, which seems very high anyway. Each time a new thread appears, this value increases, and the CPU load increases accordingly.

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

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

4 回答 4

5

一些建议:

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

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

于 2009-01-23T13:27:05.337 回答
4

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

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

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

于 2009-01-24T04:02:00.533 回答
0

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

于 2009-03-26T20:13:30.963 回答
0

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

于 2011-05-30T20:36:47.023 回答