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)
- 在窗体上添加
BackgroundWorker控件,启用WorkerReportsProgress属性 - 实现相关事件逻辑:
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
相关产品推荐
相关产品推荐

