如何在新线程中创建Timer解决PictureBox扫线卡顿问题
解决PictureBox扫线鼠标移动卡顿问题
原WinForms Timer运行在UI线程,当鼠标在PictureBox内移动时,UI线程被鼠标消息占用,导致Timer的Tick事件延迟,扫线出现卡顿。以下是两种基于后台线程的解决方案:
方案一:使用System.Threading.Timer
该Timer基于线程池运行,不会阻塞UI线程,适合定时更新逻辑。
完整代码示例
Private lineX As Integer = 0 Private updateTimer As System.Threading.Timer Private lockObj As New Object() ' 保证lineX的线程安全访问 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' 初始化Timer,每隔16ms触发一次(约60帧刷新率),立即启动 updateTimer = New System.Threading.Timer(AddressOf TimerCallback, Nothing, 0, 16) End Sub Private Sub TimerCallback(state As Object) ' 线程安全更新lineX SyncLock lockObj lineX += 5 If lineX > PictureBox1.Width Then lineX = 0 End If End SyncLock ' 跨线程刷新UI,必须通过Invoke确保在UI线程执行 If PictureBox1.InvokeRequired Then PictureBox1.Invoke(Sub() PictureBox1.Invalidate()) Else PictureBox1.Invalidate() End If End Sub Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint SyncLock lockObj e.Graphics.DrawLine(Pens.Black, New Point(lineX, 0), New Point(lineX, PictureBox1.Height)) End SyncLock End Sub Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing ' 释放Timer资源 updateTimer?.Dispose() End Sub
关键说明
System.Threading.Timer的回调在线程池线程执行,不会被UI线程的鼠标操作阻塞SyncLock用于保证lineX在多线程访问时的安全性- 使用
Invoke将UI刷新操作切换回UI线程,符合WinForms控件的线程安全要求
方案二:使用BackgroundWorker
BackgroundWorker封装了线程操作,自带进度更新的线程安全机制,适合简单的后台循环任务。
完整代码示例
Private lineX As Integer = 0 Private bgWorker As New BackgroundWorker() Private lockObj As New Object() Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load bgWorker.WorkerReportsProgress = True bgWorker.WorkerSupportsCancellation = True AddHandler bgWorker.DoWork, AddressOf BgWorker_DoWork AddHandler bgWorker.ProgressChanged, AddressOf BgWorker_ProgressChanged bgWorker.RunWorkerAsync() End Sub Private Sub BgWorker_DoWork(sender As Object, e As DoWorkEventArgs) Dim worker As BackgroundWorker = CType(sender, BackgroundWorker) ' 循环更新直到收到取消信号 While Not worker.CancellationPending SyncLock lockObj lineX += 5 If lineX > PictureBox1.Width Then lineX = 0 End If End SyncLock ' 通知UI线程刷新 worker.ReportProgress(0) ' 控制刷新间隔,约60帧 System.Threading.Thread.Sleep(16) End While End Sub Private Sub BgWorker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) ' ProgressChanged事件自动在UI线程执行,无需额外Invoke PictureBox1.Invalidate() End Sub Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint SyncLock lockObj e.Graphics.DrawLine(Pens.Black, New Point(lineX, 0), New Point(lineX, PictureBox1.Height)) End SyncLock End Sub Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing ' 关闭时取消后台任务 If bgWorker.IsBusy Then bgWorker.CancelAsync() End If End Sub
关键说明
DoWork事件在后台线程执行更新逻辑,不会占用UI线程ProgressChanged事件自动切换到UI线程,直接调用Invalidate()即可刷新- 支持通过
CancelAsync()安全终止后台任务
内容的提问来源于stack exchange,提问作者user1377723
相关产品推荐
相关产品推荐

