C# 如何正确使用.NET2.0串口.BaseStream进行异步操作
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/462698/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to correctly use .NET2.0 serial port .BaseStream for async operation
提问by Andy
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 个字节,并以 57600 的波特率返回 800 个左右的字节。
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.
最初的 CSwitch Delta(同样来自 Process Explorer)大约是 2500,无论如何这看起来非常高。每出现一个新线程,这个值就会增加,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.
}
}
回答by kgiannakakis
Some suggestions:
一些建议:
Since you are only sending 3 bytes, you could have a synchronous Write Operation. The delay wouldn't be much of an issue.
由于您仅发送 3 个字节,因此您可以进行同步写入操作。延迟不会有太大问题。
Also don't create a new AsyncCallback all the time. Create one Read and one Write AsyncCallback and use that in every begin call.
也不要一直创建新的 AsyncCallback。创建一个 Read 和一个 Write AsyncCallback 并在每个 begin 调用中使用它。
回答by Hans Passant
No need at all for 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.
根本不需要BeginWrite。您只发送 3 个字节,它们很容易放入传输缓冲区,并且您始终可以确保发送下一组时缓冲区为空。
Keep in mind that serial ports are much slower than TCP/IP connections. It is pretty likely that you end up calling BeginRead() for every single byte that you receive. 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.
请记住,串行端口比 TCP/IP 连接慢得多。您很可能最终会为收到的每个字节调用 BeginRead()。这给线程池一个很好的锻炼,你肯定会看到很多上下文切换。不太确定手柄消耗。请务必在没有附加调试器的情况下进行测试。
Trying DataReceived instead of BeginRead() is definitely something you should try. Pull instead of push, you'll use a threadpool thread when there's something happening instead of always having one active.
尝试 DataReceived 而不是 BeginRead() 绝对是您应该尝试的事情。拉而不是推,当有事情发生时,你将使用线程池线程,而不是总是有一个活动。
回答by Hans Passant
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 MegaBaud) 下,很难处理如此大量的不间断数据。
回答by mtrw
Is the response from the device always a fixed size? If so, try using SerialPort.Read
and pass the packet size. This will block, so combine it with 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
. This will immunize you against future packet size changes.
设备的响应总是固定大小吗?如果是这样,请尝试使用SerialPort.Read
并传递数据包大小。这会阻塞,因此将其与DataReceived
. 更好的是,如果响应总是以相同的字符结束,并且这个结束签名保证在数据包中是唯一的,请设置NewLine
属性并使用ReadLine
. 这将使您免受未来数据包大小变化的影响。