看不见的bug,应用卡住了

时间:2014-01-11 08:35:25

标签: .net database vb.net loops

我的应用程序已完成约95%,现在我正处于测试阶段。
我在调试文件夹中使用了.exe文件,然后使用该应用程序从gps模块的字符串突发中保存特定行以访问数据库..
这是流程

示例数据
enter image description here
从gprmc到gpgsa = 1秒间隔。所以技术上模块每秒发送三行左右。

Try  
Dim fruit As String = "$GPRMC"

            For Each line As String In RichTextBox1.Lines
                If line.Contains(fruit) Then
                    ProgressBar1.Value = 0
                    txt = line.Split(","c)
                    Insert()
                End If
            Next
Catch ex As Exception
        sPort.Close()
        MessageBox.Show("There had been no data received.", Me.Text, MessageBoxButtons.RetryCancel)
        Call btnStartTimer_Click(sender, New EventArgs)
    End Try

Minute_Tick下,每60秒,Second.Start,即上述代码)。每分钟,我清除RichTextBox然后在2秒后(rtb再次填充)我得到$GPRMC的行,.Split到数组〜txt() 。从此以后,我将单词中的昏迷分隔开来。

现在我将它添加到数据库中:

' Now inside a Using block
           If txt(3) = String.Empty Then
                .AddWithValue("@lat", 0)
            Else
                Dim la As Double = Double.Parse(txt(3).Substring(0, 2)) + Double.Parse(txt(3).Substring(2)) / 60.0
                .AddWithValue("@lat", la)
            End If

txt(3)是Latitude DMS格式,因此我添加了一些转换代码段。它工作正常,但我添加了一个条件,以确保它不会计算null或0值。

更新(我无法真正理解这段代码,因为我只是复制了它,但它适用于我创建的最后一个应用。)

Delegate Sub SetTextCallback(ByVal [text] As String)
Dim x As New SetTextCallback(AddressOf ReceivedText)
Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles sPort.DataReceived
    ReceivedText(sPort.ReadExisting)
End Sub
Private Sub ReceivedText(ByVal [text] As String)
    If Me.RichTextBox1.InvokeRequired Then
        Me.Invoke(x, New Object() {(text)})
    Else
        Me.RichTextBox1.Text &= [text]
    End If
End Sub

我发现的麻烦
我正在经历像线程睡眠这样的事情。应用程序挂起,无法单击,无法关闭,但它在任务管理器上“运行”,所以我认为它处于无限循环中,或者我错了?我结束了它在任务管理器上的关闭,并再次打开它。运行顺畅..然后,它会卡住。
我在思考,直到现在,是什么原因造成的?我认为它是因为模块不能总是得到一个信号,所以它什么都不返回..但如果模块没有给出坐标,它会在DB (上面的代码)中保存0,所以没有问题那。

你们能帮帮我罪魁祸首吗?我在第三天,所以我决定需要一些帮助。谢谢。如果您需要任何或一些澄清,请告诉我。

1 个答案:

答案 0 :(得分:5)

If Me.RichTextBox1.InvokeRequired Then
    Me.Invoke(x, New Object() {(text)})
Else
    Me.RichTextBox1.Text &= [text]
End If

您的代码正在执行此操作:

enter image description here

这是 firehose 问题。问题始于SerialPort.ReadExisting()调用。通常返回一个或两个字符,串口很慢。在9600波特的常见波特率设置下,每秒可获得1000个字符,因此您可以有效地将新文本每秒大约500次添加到RichTextBox中,无论是给予还是接受。

这会强制RichTextBox重新分配其内部缓冲区,该缓冲区存储文本并为额外添加的字符腾出空间,然后将旧缓冲区中的所有现有文本复制到新缓冲区并附加新文本。然后更新屏幕。

当您第一次启动程序时,这非常顺利,RichTextBox尚未包含太多文本。但是逐渐变得越来越贵,越来越多的角色必须被复制。

直到你到达复制开始花费太多时间的关键点,比你调用Me.Invoke()的时间更长。 UI线程现在开始落后,永远无法跟上。就像试图从消防水管中喝水一样。一旦完成复制内部缓冲区,就需要调度另一个调用请求,强制重新分配缓冲区。

UI线程现在停止处理其正常的低优先级职责。其中包括更新屏幕和处理输入事件。你注意到你的程序被冻结了,好像它已经陷入僵局。 Windows将主窗口替换为“无响应”的重影窗口,并且敲击鼠标或键盘无效。您所能做的就是使用调试器或任务管理器终止程序并重新启动它。哪个工作正常,RichTextBox再次有一个空缓冲区,复制再次便宜。

请注意处理字符串时这是一般问题。 .NET Framework具有 StringBuilder 类来解决它。然而,这不适用于RichTextBox,您需要找到不同的解决方案。

  

所以你有效地向RichTextBox添加新文本大约每秒500次

您需要解决 问题。以该速率向RTB添加文本毫无意义,没有人能够观察到如此高的速率。每秒添加20次文本已足够快,当你比这更快时,它开始看起来像模糊。或者换句话说,现在你做的比你需要快25倍。让RTB和UI线程都很难跟上这个速度。

另请注意,这可以解释您的解析问题。您正在解析不完整的文本行。因此,对此的一阶修复是仅在从串行端口获得完整的文本行时调用。这很容易实现:

Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles sPort.DataReceived
    ReceivedText(sPort.ReadLine)
End Sub

换句话说,您要求串行端口返回整行文本而不是仅返回一个或两个字符。这会自动大大降低调用率并解决您的解析问题。

你还有两件事要做。当程序运行足够长时,程序仍会挂起。您确实必须限制RTB中的文本量。当它存储超过65000个字符时,只丢掉一半。并且你必须删除对Close()方法的调用,这将导致真正的死锁,因为如果DataReceived调用仍然停留在ReadLine()调用中,则端口无法关闭。使用BeginInvoke()而不是Invoke(),这样可以减少死锁的危险。