我尝试以异步方式从串行端口读取数据,请记住,操作所花费的时间不得超过指定的时间段。我使用的代码:
private async Task<int> Read(byte[] buffer, int offset, int length)
{
Console.WriteLine("Reading response to buffer of size {0}, offset {1}, max to cnt {2}", buffer.Length, offset, length);
int totalBytesRead = 0;
Console.WriteLine("Begin reading...");
while (true)
{
Console.WriteLine("Re-entering read");
int bytesRead = 0;
using (CancellationTokenSource cts = new CancellationTokenSource(new TimeSpan(0, 0, 0, 0, 333))) // 333 ms
{
Console.WriteLine("Waiting for read to complete...");
int tmpOffset = offset + totalBytesRead;
int tmpLength = length - totalBytesRead;
Console.WriteLine("tmpOffset: {0}, tmpLength: {1}", tmpOffset, tmpLength);
try
{
bytesRead = await mPort.BaseStream.ReadAsync(buffer, tmpOffset, tmpLength, cts.Token);
}
catch (Exception e)
{
Console.WriteLine("Exception during read: {0}, stack trace: {1}", e.Message, e.StackTrace);
break;
}
Console.WriteLine("Read completed");
}
totalBytesRead += bytesRead;
Console.WriteLine("Bytes read: {0}, totalBytesRead: {1}, buffer: {2}", bytesRead, totalBytesRead, BitConverter.ToString(buffer, 0, buffer.Length));
if (bytesRead == 0)
{
break;
}
}
Console.WriteLine("Read finished");
return totalBytesRead;
}
以下代码将这些消息返回到控制台:
Reading response to buffer of size 256, offset 0, max to cnt 256
Begin reading...
Re-entering read
Waiting for read to complete...
tmpOffset: 0, tmpLength: 256
Read completed
Bytes read: 1, totalBytesRead: 1, buffer: 52-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
Re-entering read
Waiting for read to complete...
tmpOffset: 1, tmpLength: 255
Read completed
Bytes read: 8, totalBytesRead: 9, buffer: 52-45-4A-30-30-36-4A-44-52-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
Re-entering read
Waiting for read to complete...
tmpOffset: 9, tmpLength: 247
最后一条消息挂起后。当然(应该)使用超过333毫秒的超时时间。对我来说,令牌似乎从未被取消过,或者至少从未捕获过这种取消。我99%确信该设备不会再发送任何数据,所以这就是我指望触发超时的原因。 我是C#的新手,所以也许有些事情我不太了解。您能看到没有在此处取消任务的任何原因吗?
我试图复制和调整this文章中介绍的方法。
答案 0 :(得分:1)
这是因为ReadAsync方法的internal implementation(第417行)。基本上,对方法的入口点处的cancellationToken进行检查,但随后它(cancellationToken)不会传递给后续调用(“一直”)。因此,如果您很幸运在执行执行到ReadAsync的调用之前就已取消,则可以得到预期的行为。
我不会就他们为什么选择不使其可取消进行辩论(在其他帖子上,我现在不记得了,我读到这是安全预防措施,因此不要使缓冲区处于某种时髦状态)。
您可以做的是使用某种计时器将呼叫包装起来,或者对我有用:
cancellationToken.Register(()=> mport.Dispose());
因为我想要的是永远结束事情。
这给我带来了
我捕获并处理的IOException:由于线程退出或应用程序请求,I / O操作已中止。
(在我的情况下,我抛出了OperationCanceledException-但仅针对此IOException,没有)。
关于SerialPort流通信还有更多讨厌的东西。其他一些有用的东西: http://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport
答案 1 :(得分:1)
我最近遇到了同样的问题,试图与一些通过串行线进行通信的旧式嵌入式仪器进行通信。我的解决方案是使用一组扩展方法,如下所示:我不处理整个端口,关闭基本流似乎足够,然后如果在取消时将其打开,则我将立即重新打开它。
public static class SerialAsyncExtensions
{
public static async Task<int> ReadSerialAsync(this SerialPort f_Serial, byte[] f_Buffer, int f_Offset, int f_Count, int f_Delay)
{
if (f_Delay > 0)
await Task.Delay(f_Delay);
return await f_Serial.BaseStream.ReadAsync(f_Buffer, f_Offset, f_Count);
}
public static async Task<int> ReadSerialAsync(this SerialPort f_Serial, byte[] f_Buffer, int f_Offset, int f_Count, int f_Delay, CancellationToken f_Token)
{
CancellationTokenRegistration l_Token_Registration = f_Token.Register((param) =>
{
SerialPort l_Serial = (SerialPort)param;
bool l_Was_Open = l_Serial.IsOpen;
l_Serial.BaseStream.Close();
if (l_Was_Open) l_Serial.Open();
}, f_Serial);
if (f_Delay > 0)
await Task.Delay(f_Delay, f_Token);
try
{
int l_Result = await f_Serial.BaseStream.ReadAsync(f_Buffer, f_Offset, f_Count, f_Token);
return l_Result;
}
catch(System.IO.IOException Ex)
{
throw new OperationCanceledException("ReadSerialAsync operation Cancelled.", Ex);
}
finally
{
l_Token_Registration.Dispose();
}
}
public static async Task WriteSerialAsync(this SerialPort f_Serial, byte[] f_Buffer, int f_Offset, int f_Count)
{
await f_Serial.BaseStream.WriteAsync(f_Buffer, f_Offset, f_Count);
}
public static async Task WriteSerialAsync(this SerialPort f_Serial, byte[] f_Buffer, int f_Offset, int f_Count, CancellationToken f_Token)
{
CancellationTokenRegistration l_Token_Registration = f_Token.Register((param) =>
{
SerialPort l_Serial = (SerialPort)param;
bool l_Was_Open = l_Serial.IsOpen;
l_Serial.BaseStream.Close();
if (l_Was_Open) l_Serial.Open();
}, f_Serial);
try
{
await f_Serial.BaseStream.WriteAsync(f_Buffer, f_Offset, f_Count, f_Token);
}
catch(System.IO.IOException Ex)
{
throw new OperationCanceledException("WriteSerialAsync operation Cancelled.", Ex);
}
finally
{
l_Token_Registration.Dispose();
}
}
}