应用程序冻结,但不崩溃? (视觉基础)

Application freezes, but not crashes? (VISUAL BASIC)

所以我一直在为学校做一个项目,其中一部分必须计算射弹运动的坐标,然后将其添加到一系列点的图表中并绘制在应用程序上。唯一的问题是,如果速度太高(这将增加飞行的总时间和航程,导致坐标集更大),应用程序会冻结 - 但不会崩溃。它没有显示任何错误,我通常会使用 Visual Studio 2013.

我会附上这个过程的代码,谁能找出它冻结的原因and/or如何解决?

我只是一个初学者,所以对编程了解不多,任何帮助将不胜感激。

For x As Integer = 0 To totalTime Step 0.01
    Dim xPos As Double = findXLocation(velocity, angle, x)
    Dim yPos As Double = findYLocation(velocity, angle, x, elevation, totalTime, heightOfProjectile, flightRange)

    Chart1.Series("Projection").Points.AddXY(xPos, yPos)

    Dim label As New CalloutAnnotation

    With label
        Chart1.Annotations.Add(label)
    End With
Next 

我发现加速计算的一种方法是将步长(最初为 0.01)降低到更高的值,这意味着将完成更少的计算。但这会导致很多 If 语句,因为 case 语句也会导致这些点的计算冻结。我还尝试设置一个名为 'increment' 的变量设置为步长值。这可以用许多不同的方式计算 - 我最初尝试 increment = totalTime / 1000,但无论输入什么速度,这都会再次导致应用程序完全冻结。

findYLocation 和 findXLocation 也可能有用,附在下面。

Public Function findYLocation(ByVal velocity As Double, ByVal angle As Double, ByVal time As Double, ByVal elevation As Double, ByVal totalTime As Double, ByVal heightOfProjectile As Double, ByVal flightRange As Double) As Double
    Dim y As Double

    y = -(0.5 * gConstant * sq(time)) + (findVerticalVelocity(velocity, angle) * time) + elevation
    ' y = Math.Round(y, 1)

    If y < 0 Then
        y = 0
    End If

    Return y
End Function

Public Function findXLocation(ByVal velocity As Double, ByVal angle As Double, ByVal time As Double) As Double
    Dim x As Double

    x = findHorizontalVelocity(velocity, angle) * time
    ' x = Math.Round(x, 1)

    If x < 0 Then
        x = 0
    End If

    Return x
End Function

您可以尝试暂停程序并检查导致程序冻结的操作(左侧按钮,Visual Studio 中停止按钮的旁边)。

您也可以尝试在您的代码中添加一些日志记录,以查看问题实际出在哪里。

Console.WriteLine("Logging")

甚至计算时间(有关详细信息,请参阅 https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch?view=netframework-4.8

问题出在这一行

For x As Integer = 0 To totalTime Step 0.01

x 是一个整数,当你给它加上 0.01 时,它永远不会增加。使 x 成为 Double。实际上,让编译器找出正确的类型可以让您省去更多麻烦

For x = 0 To totalTime Step 0.01 ' it is now Double

并且在代码文件的顶部添加 Option Strict On 无论如何都会为您找到问题

For x As Integer = 0 To totalTime Step 0.01

Option Strict On disallows implicit conversions from 'Double' to 'Integer'

另一个答案和评论建议您调试代码。当然,调试会帮助你解决这个问题。如果我是你,我会让它 运行,在 For 循环中放置一个断点,然后检查发生了什么。很明显循环变量没有递增。

根据另一个答案,您可以在循环中添加一个 Console.WriteLine。这条线在循环内运行良好

Console.WriteLine($"Inside For loop, x:{x}, xPos:{xPos}, yPos:{yPos}")

这会说

Inside For loop, x:0, xPos:0, yPos:10
Inside For loop, x:0, xPos:0, yPos:10
Inside For loop, x:0, xPos:0, yPos:10
Inside For loop, x:0, xPos:0, yPos:10
Inside For loop, x:0, xPos:0, yPos:10

您期望值发生变化的地方。一旦是 Double

他们就会这样做

Inside For loop, x:0, xPos:0, yPos:10
Inside For loop, x:0.01, xPos:0.0707106781186548, yPos:11.6202267316012
Inside For loop, x:0.02, xPos:0.14142135623731, yPos:12.3327679741871
Inside For loop, x:0.03, xPos:0.212132034355964, yPos:12.8959725661313
Inside For loop, x:0.04, xPos:0.282842712474619, yPos:13.3818748194396

我仍然不认为这是完整的。

没有理由在 UI 上执行 运行 整个循环。所以把你的处理和 UI 分开。有很多方法可以实现这一点。一种是使用 Async/Await,我认为这是最简单的方法。了解当您在 UI 之外操作时,您将需要调用回 UI 的调用以在那里进行任何更新。

您创建的最大性能影响是在图表中为每 0.01 时间增量添加一个注释。这真的有必要吗?不添加注释,绘图非常快。有了注释,它很快就会变慢。

y 为零后还需要继续处理吗?我添加了一些逻辑以在它发生时退出循环。

请注意,Await 会从 UI 发送代码,因此您的 UI 将在 运行ning 期间进行交互。这是一些演示这一点的代码。请注意,注释每 0.1 秒而不是 0.01 添加一次。他们被有效地摧毁了。您可以随心所欲地使用它,让它按照您的需要工作。

Private gConstant As Double = 9.8
Private totalTime As Double
Private velocity As Double
Private angle As Double
Private elevation As Double
Private heightOfProjectile As Double
Private flightRange As Double

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    totalTime = 100
    velocity = 100
    angle = Math.PI / 4
    elevation = 100
    heightOfProjectile = 10
    flightRange = 10
    Chart1.ChartAreas(0).AxisX.Minimum = 0
    Chart1.Series("Projection").ChartType = SeriesChartType.FastLine
    Await Task.Factory.StartNew(AddressOf getPoints)
End Sub

Private Sub getPoints()
    For x As Double = 0 To totalTime Step 0.01
        Dim xPos As Double = findXLocation(velocity, angle, x)
        Dim yPos As Double = findYLocation(velocity, angle, x, elevation, totalTime, heightOfProjectile, flightRange)
        Dim l = x
        Chart1.Invoke(
            Sub()
                Chart1.Series("Projection").Points.AddXY(xPos, yPos)
                If CInt(100 * l) Mod 10 = 0 Then Chart1.Annotations.Add(New CalloutAnnotation())
            End Sub)
        Console.WriteLine($"Inside For loop, x:{x}, xPos:{xPos}, yPos:{yPos}")
        If yPos = 0 Then Exit For
    Next
End Sub

Public Function findYLocation(ByVal velocity As Double, ByVal angle As Double, ByVal time As Double, ByVal elevation As Double, ByVal totalTime As Double, ByVal heightOfProjectile As Double, ByVal flightRange As Double) As Double
    Return Math.Max(0, -0.5 * gConstant * sq(time) + findVerticalVelocity(velocity, angle) * time + elevation)
End Function

Private Function sq(value As Double) As Double
    Return value ^ 2
End Function

Public Function findXLocation(ByVal velocity As Double, ByVal angle As Double, ByVal time As Double) As Double
    Return Math.Max(0, findHorizontalVelocity(velocity, angle) * time)
End Function

Private Function findVerticalVelocity(velocity As Double, angle As Double) As Double
    Return Math.Sin(angle) * velocity
End Function

Private Function findHorizontalVelocity(velocity As Double, angle As Double) As Double
    Return Math.Cos(angle) * velocity
End Function