c# - usar - serial port visual studio 2017
Cómo utilizar correctamente el puerto serie.NET2.0.BaseStream para la operación asíncrona (4)
Estoy intentando usar la propiedad .BaseStream del .NET2.0 SerialPort para hacer lecturas y escrituras asincrónicas (BeginWrite / EndWrite, BeginRead / EndRead).
Tengo cierto éxito en esto, pero después de un tiempo, noté (usando Process Explorer) un aumento muy gradual en los Handles que usa la aplicación, y de vez en cuando un hilo adicional, que también aumenta el conteo de Handle.
La tasa de cambio de contexto también aumenta cada vez que aparece un nuevo hilo.
La aplicación envía constantemente 3 bytes a un dispositivo PLC, y obtiene 800 o más bytes a cambio, y lo hace a una velocidad en baudios de 57600.
El CSwitch Delta inicial (nuevamente, desde Process Explorer) es alrededor de 2500, lo que parece muy alto de todos modos. Cada vez que aparece un nuevo hilo, este valor aumenta y la carga de la CPU aumenta en consecuencia.
Espero que alguien haya hecho algo similar y pueda ayudarme, o incluso decir ''En nombre de Dios, no lo hagas de esa manera''.
En el siguiente código, ''this._stream'' se obtiene de SerialPort.BaseStream, y CommsResponse es una clase que uso como el objeto de estado IAsyncresult.
Este código es común a una conexión TCP que hago como alternativa al uso del puerto serie (tengo una clase base CommsChannel, con un canal serial y TCP derivado de ella) y no tiene ninguno de estos problemas, así que estoy razonablemente esperanzado que no hay nada de malo con la clase CommsResponse.
Cualquier comentario recibido con gratitud
/// <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.
}
}
Algunas sugerencias:
Como solo está enviando 3 bytes, podría tener una operación de escritura sincrónica. La demora no sería un gran problema.
Además, no cree un nuevo AsyncCallback todo el tiempo. Cree una AsyncCallback de lectura y otra de escritura y úsela en cada llamada de inicio.
No es necesario en absoluto para BeginWrite. Solo envía 3 bytes, encajarán fácilmente en el búfer de transmisión y siempre estará seguro de que el búfer está vacío cuando envíe el siguiente conjunto.
Tenga en cuenta que los puertos serie son mucho más lentos que las conexiones TCP / IP. Es bastante probable que termine llamando a BeginRead () por cada byte que reciba. Eso le da al grupo de subprocesos un buen entrenamiento, definitivamente vería muchos cambios de contexto. No estoy tan seguro sobre el consumo de manejo. Asegúrese de probarlo sin el depurador adjunto.
Probar DataReceived en lugar de BeginRead () es definitivamente algo que debes probar. Tire en lugar de presionar, usará un hilo de subprocesos cuando hay algo que sucede en lugar de tener siempre uno activo.
¿Es posible tomar los datos que ingresan desde el puerto serie y enviarlos directamente a un archivo? A altas tasas de baudios (1 MegaBaud) es difícil manejar esta cantidad de datos sin paradas.
¿La respuesta del dispositivo siempre es de tamaño fijo? Si es así, intente usar SerialPort.Read
y pase el tamaño del paquete. Esto bloqueará, así que combínelo con DataReceived
. Mejor aún, si la respuesta siempre termina con el mismo carácter (s), y se garantiza que esta firma final es única en el paquete, configure la propiedad NewLine
y use ReadLine
. Esto lo inmunizará contra futuros cambios en el tamaño del paquete.