You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FPS测试中Stopwatch阻塞程序致进度条无响应的解决方案求助

解决FPS测试时UI阻塞无法更新进度条的问题

问题分析

你当前的FPS统计逻辑在主线程中执行同步循环,该循环会持续占用主线程5秒,导致UI线程无法处理进度条更新、用户交互等消息,不仅进度条无法正常填充,整个程序还会出现假死状态。

解决思路

核心是把FPS统计的耗时操作放到后台线程执行,让主线程专注处理UI更新。注意:WinForms中不能直接在后台线程操作UI控件,必须通过Invoke/BeginInvoke回到主线程更新UI。

以下是两种可行的实现方案:


方案1:使用Task异步执行(推荐,.NET 4.5+)

Private Async Function MeasureFPSAsync() As Task(Of Integer)
    Dim frames As Integer = 0
    Dim stopwatch As New Stopwatch()
    Dim bmp As New Bitmap(Me.Width, Me.Height, PixelFormat.Format32bppArgb)
    Dim g As Graphics = Graphics.FromImage(bmp)

    ' 后台线程执行FPS统计
    Await Task.Run(Sub()
                       stopwatch.Start()
                       Do While stopwatch.Elapsed.TotalSeconds < 5
                           g.DrawString(frames.ToString(), New Font("Arial", 12), Brushes.White, 0, 0)
                           frames += 1

                           ' 实时计算并更新进度条
                           Dim progressPercentage As Integer = CInt((stopwatch.Elapsed.TotalSeconds / 5) * 100)
                           UpdateProgressBar(progressPercentage)
                       Loop
                       stopwatch.Stop()
                   End Sub)

    ' 回到主线程更新FPS结果
    Dim fps As Integer = frames \ 5
    BunifuTextBox1.Invoke(Sub() BunifuTextBox1.Text = fps.ToString())

    ' 释放资源
    g.Dispose()
    bmp.Dispose()
    Return fps
End Function

' UI安全的进度条更新方法
Private Sub UpdateProgressBar(percentage As Integer)
    If BunifuProgressBar1.InvokeRequired Then
        BunifuProgressBar1.Invoke(Sub() UpdateProgressBar(percentage))
    Else
        BunifuProgressBar1.Value = Math.Min(percentage, 100)
    End If
End Sub

调用示例(按钮点击事件):

Private Async Sub btnStartTest_Click(sender As Object, e As EventArgs) Handles btnStartTest.Click
    Await MeasureFPSAsync()
End Sub

方案2:使用BackgroundWorker(兼容旧版.NET)

  1. 在窗体上添加BackgroundWorker控件,启用WorkerReportsProgress属性
  2. 实现相关事件逻辑:
Private Sub bgwFPS_DoWork(sender As Object, e As DoWorkEventArgs) Handles bgwFPS.DoWork
    Dim frames As Integer = 0
    Dim stopwatch As New Stopwatch()
    Dim bmp As New Bitmap(Me.Width, Me.Height, PixelFormat.Format32bppArgb)
    Dim g As Graphics = Graphics.FromImage(bmp)

    stopwatch.Start()
    Do While stopwatch.Elapsed.TotalSeconds < 5
        g.DrawString(frames.ToString(), New Font("Arial", 12), Brushes.White, 0, 0)
        frames += 1

        ' 报告当前进度
        Dim progress As Integer = CInt((stopwatch.Elapsed.TotalSeconds / 5) * 100)
        bgwFPS.ReportProgress(progress)
    Loop
    stopwatch.Stop()

    ' 释放资源并返回结果
    g.Dispose()
    bmp.Dispose()
    e.Result = frames \ 5
End Sub

Private Sub bgwFPS_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles bgwFPS.ProgressChanged
    ' 主线程更新进度条
    BunifuProgressBar1.Value = e.ProgressPercentage
End Sub

Private Sub bgwFPS_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles bgwFPS.RunWorkerCompleted
    ' 主线程更新FPS结果
    BunifuTextBox1.Text = e.Result.ToString()
End Sub

调用示例:

Private Sub btnStartTest_Click(sender As Object, e As EventArgs) Handles btnStartTest.Click
    bgwFPS.RunWorkerAsync()
End Sub

额外注意事项

  • 必须调用Dispose()释放Graphics、Bitmap等资源,避免内存泄漏
  • 如果需要实际渲染到窗体而非内存Bitmap,可考虑用Timer控件定时统计帧数,这种方式也不会阻塞主线程

内容的提问来源于stack exchange,提问作者Yaamruo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 07:32:51