200 块有偿求助 C#高速读取串口,导致软件闪退的问题。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hulalahei
V2EX    C#

200 块有偿求助 C#高速读取串口,导致软件闪退的问题。

  •  
  •   hulalahei 119 天前 2334 次点击
    这是一个创建于 119 天前的主题,其中的信息可能已经有所发展或是发生改变。

    开发框架是 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 红包感谢。

    23 条回复    2025-06-26 01:27:23 +08:00
    muyiluop
        1
    muyiluop  
       119 天前
    int numRead = await _serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length);
    这里为啥要异步读呢
    luojianxhlxt
        2
    luojianxhlxt  
       119 天前
    我现在都是用 TouchSocket

    要不你试试?

    https://touchsocket.net/docs/current/serialportclient
    hulalahei
        3
    hulalahei  
    OP
       119 天前
    @muyiluop 我之前也尝试过直接 read ,可以正常读数,但是一会读数就卡死了,_serialPort.BytesToRead 会不断上升。正常的数据是 5 个字节的,有的时候会突然一下返回几百个字节。
    Jscroop
        4
    Jscroop  
       119 天前
    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();
    }
    muyiluop
        5
    muyiluop  
       119 天前
    @hulalahei #3 如果你是连续不断的接收数据的话,可以不管每次读到多少,直接丢给处理方法去处理,这里只管读。你的处理方法再去按 5 个字节或者你的数据结果去处理。返回几百个是因为你的 buffer 长度就是 512 。串口的数据他是数据流的概念,不像 udp 那种数据包的概念,发送端发了一包,接收端就接收 1 包
    hahiru
        6
    hahiru  
       119 天前   1
    DataReceived 事件触发的速度超过了 OnSerialDataReceived 方法中 ReadAsync 完成的速度。导致前一个 ReadAsync 还没结束下一个 DataReceived 事件又触发了,从而尝试在同一个 BaseStream 上发起第二次 ReadAsync 。
    简单修复就是加锁,遇到竞争就丢弃此次事件,等待下一次。
    GeruzoniAnsasu
        7
    GeruzoniAnsasu  
       119 天前
    根本原因是缓冲区和吞吐量太小,导致同步读会堆积数据,异步读会导致回调没完成就重入了

    为什么每次回调不直接读 BytesToRead 个字节?
    695975931
        8
    695975931  
       119 天前
    有个博主对 c# 的 debug 很有经验,你可以加他看看。他的公众号:一线码农聊技术。v:aXhjaHVhbmc=
    hulalahei
        9
    hulalahei  
    OP
       119 天前
    @GeruzoniAnsasu 回调直接读 BytesToRead 个字节我也试过,还是会报同样的错。
    hulalahei
        10
    hulalahei  
    OP
       119 天前
    @GeruzoniAnsasu 还有个问题,就是同步读数据堆积了,无法获取到实时的数据。
    hifeng
        11
    hifeng  
       119 天前
    类里面建个队列,ConcurrentQueue<byte[]> datas =new ConcurrentQueue<byte[]>(2000)
    回调直接读 BytesToRead 到 buff,
    添加到队列 datas.Enqueue(buff) 就返回;
    另外一个线程去处理 datas;
    hulalahei
        12
    hulalahei  
    OP
       119 天前
    @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,然后越来越大。
    hulalahei
        13
    hulalahei  
    OP
       119 天前
    @hifeng 我试试
    hulalahei
        14
    hulalahei  
    OP
       119 天前
    @hahiru 好的,我试试。
    andykuen959595
        15
    andykuen959595  
       119 天前
    @695975931 #8 哈哈哈 大佬都发朋友圈了
    FreeWong
        16
    FreeWong  
       119 天前
    DataReceived 事件在同一时间只能引发一个,如果你的设备发送的数据过快,而你在 DataReceived 事件中去做协议完整性的拆分后,再解析出各字段的值, 可能就无法再引发 DataReceived 事件。导致缓冲区堆积大量数据也会导致数据丢失等问题.
    FreeWong
        17
    FreeWong  
       119 天前
    async ,await 在这里是不需要的
    hulalahei
        18
    hulalahei  
    OP
       119 天前
    @hahiru 老哥的这个方法好像是可以的,一下午还没闪退。晚上还没闪退的话,200 红包就给你啦。红包咋给你比较方便。
    hulalahei
        19
    hulalahei  
    OP
       119 天前
    ysc3839
        20
    ysc3839  
       118 天前 via Android
    不懂 C#,但是 ReadAsync 不是直到有数据才返回吗?那直接循环 read 就完事了啊,为啥还要加事件?
    hahiru
        21
    hahiru  
       118 天前
    @hulalahei #18 没事不用了。
    hulalahei
        22
    hulalahei  
    OP
       117 天前
    @ysc3839 我一开始就是循环 read 的,返回的数据速度上升之后,大概十分钟就会出现同样的问题。可能和设备有一定的关系,这个设备十几年前买的。但是新设备同样有这个问题,只是出现的频率低一点。
    kevin100702
        23
    kevin100702  
       105 天前 via Android
    system.io.pipeline 才是终极解决方案
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2825 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 13:35 PVG 21:35 LAX 06:35 JFK 09:35
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86