开发框架是 Avaloina 11.2.2 + .NET 8.0 ,包是 System.IO.Ports 9.0.0-preview
问题描述:电脑通过串口读取硬件的数据,设备会连续不断返回数据,如果设备每秒返回 50 次数据,软件就不会闪退。如果设备返回数据的速度超过 200 次每秒,就有一定的概率会出现闪退的问题,闪退的频率大概就是 1-2 小时出现一次,偶尔会 10 分钟内连续出现两次。我换过不同的设备,只要是读取速度过快,都会出现相同的问题。
读取数据的代码片段是:
_serialPort = new SerialPort();
_serialPort.Parity = Parity.None;
_serialPort.DataBits = 8;
_serialPort.StopBits = StopBits.One;
_serialPort.RtsEnable = true;
_serialPort.Handshake = Handshake.None;
_serialPort.ReadTimeout = 5;
_serialPort.WriteTimeout = 5;
_serialPort.ReceivedBytesThreshold = 1;
_serialPort.DataReceived += OnSerialDataReceived;
private async void OnSerialDataReceived(object sender, SerialDataReceivedEventArgs e){
var buffer = new byte[512]; // 最多读 10 字节
int numRead = await _serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length);
}
闪退的时候报错信息:
[06/02/2025 01:20:02] InvalidOperationException: Argument_NativeOverlappedAlreadyFree
at System.Threading.ThreadPoolBoundHandle.OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, UInt32 ioResult, UIntPtr numberOfBytesTransferred, IntPtr ioPtr)
第一个帮助我解决问题的小伙伴,200 红包感谢。
1
muyiluop 49 天前
int numRead = await _serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length);
这里为啥要异步读呢 |
![]() |
2
luojianxhlxt 49 天前
|
3
hulalahei OP @muyiluop 我之前也尝试过直接 read ,可以正常读数,但是一会读数就卡死了,_serialPort.BytesToRead 会不断上升。正常的数据是 5 个字节的,有的时候会突然一下返回几百个字节。
|
4
Jscroop 49 天前
Timeout 有调整大点试过吗?也有可能是 async 又返回 void 的问题,可以这样试试看
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { var buffer = new byte[512]; // 最多读 10 字节 int numRead = _serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length).GetAwaiter().GetResult(); } |
5
muyiluop 49 天前
@hulalahei #3 如果你是连续不断的接收数据的话,可以不管每次读到多少,直接丢给处理方法去处理,这里只管读。你的处理方法再去按 5 个字节或者你的数据结果去处理。返回几百个是因为你的 buffer 长度就是 512 。串口的数据他是数据流的概念,不像 udp 那种数据包的概念,发送端发了一包,接收端就接收 1 包
|
![]() |
6
hahiru 49 天前 ![]() DataReceived 事件触发的速度超过了 OnSerialDataReceived 方法中 ReadAsync 完成的速度。导致前一个 ReadAsync 还没结束下一个 DataReceived 事件又触发了,从而尝试在同一个 BaseStream 上发起第二次 ReadAsync 。
简单修复就是加锁,遇到竞争就丢弃此次事件,等待下一次。 |
7
GeruzoniAnsasu 49 天前
根本原因是缓冲区和吞吐量太小,导致同步读会堆积数据,异步读会导致回调没完成就重入了
为什么每次回调不直接读 BytesToRead 个字节? |
![]() |
8
695975931 49 天前
有个博主对 c# 的 debug 很有经验,你可以加他看看。他的公众号:一线码农聊技术。v:aXhjaHVhbmc=
|
9
hulalahei OP @GeruzoniAnsasu 回调直接读 BytesToRead 个字节我也试过,还是会报同样的错。
|
10
hulalahei OP @GeruzoniAnsasu 还有个问题,就是同步读数据堆积了,无法获取到实时的数据。
|
11
hifeng 49 天前
类里面建个队列,ConcurrentQueue<byte[]> datas =new ConcurrentQueue<byte[]>(2000)
回调直接读 BytesToRead 到 buff, 添加到队列 datas.Enqueue(buff) 就返回; 另外一个线程去处理 datas; |
12
hulalahei OP @muyiluop #5 意思是把读和处理分开吗?我记得我试过只读数据,压根不处理,也会造成数据的积累。比如改成这样之后
int toRead = _serialPort.BytesToRead; if (toRead <= 0) return; var buffer = new byte[toRead]; // 最多读 10 字节 int numRead = _serialPort.Read(buffer, 0, buffer.Length); 正常是返回 5 字节,一开始会是正常 5 5 5 5 5 5 5 这样返回,后续就会变成 5 5 5 120 160,然后越来越大。 |
![]() |
15
andykuen959595 49 天前
@695975931 #8 哈哈哈 大佬都发朋友圈了
|
16
FreeWong 49 天前
DataReceived 事件在同一时间只能引发一个,如果你的设备发送的数据过快,而你在 DataReceived 事件中去做协议完整性的拆分后,再解析出各字段的值, 可能就无法再引发 DataReceived 事件。导致缓冲区堆积大量数据也会导致数据丢失等问题.
|
17
FreeWong 49 天前
async ,await 在这里是不需要的
|
![]() |
20
ysc3839 49 天前 via Android
不懂 C#,但是 ReadAsync 不是直到有数据才返回吗?那直接循环 read 就完事了啊,为啥还要加事件?
|
22
hulalahei OP @ysc3839 我一开始就是循环 read 的,返回的数据速度上升之后,大概十分钟就会出现同样的问题。可能和设备有一定的关系,这个设备十几年前买的。但是新设备同样有这个问题,只是出现的频率低一点。
|
23
kevin100702 36 天前 via Android
system.io.pipeline 才是终极解决方案
|