我试图使用.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.
}
}
答案 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
。这将使您免受未来数据包大小变化的影响。