如何在C#中实现Timer与Bitmap刷新的同步?
解决Timer与相机帧接收的同步问题,稳定更新直方图
我完全懂你的困扰——一边是相机后台线程不停推送新帧,一边是Timer定时要更新直方图,两个操作碰在一起就容易因为共享资源冲突导致图表抽风。别着急,咱们一步步来解决这个线程同步的问题:
核心思路
问题根源在于两个线程(相机回调线程 + Timer触发的UI线程)同时访问/修改同一份图像数据,导致数据不一致。我们需要做两件事:
- 用线程安全的方式保存最新的帧数据,避免冲突
- 把直方图计算放到后台线程,别卡UI,计算完再回到UI线程更新图表
具体代码修改
首先,先定义一个线程安全的变量来存最新的帧,加锁保护:
// 锁对象,用来保护帧数据的访问 private readonly object _frameLock = new object(); // 保存最新收到的相机帧,只存最新的,旧的直接覆盖 private Frame _latestFrame;
然后修改OnFrameReceived回调,只负责更新最新帧,不要在这里计算直方图(之前卡就是因为这个):
private void OnFrameReceived(Camera camera, Frame frame) { // 加锁确保只有一个线程能修改_latestFrame lock (_frameLock) { // 注意:这里一定要复制帧数据!相机可能会复用Frame对象,直接赋值会导致后续处理时数据被覆盖 // 如果你的Frame类有Clone方法,直接用;没有的话就手动复制像素数据到新的容器(比如字节数组/Bitmap) _latestFrame = frame.Clone(); } // 把帧放回队列,让相机继续采集 camera.QueueFrame(frame); }
接下来修改Timer的Tick事件,安全获取最新帧后,后台计算直方图再更新UI:
private void histTimer_Tick(object sender, EventArgs e) { Frame frameToProcess = null; // 加锁安全取出最新帧,取出后清空_latestFrame,避免重复处理同一帧 lock (_frameLock) { if (_latestFrame != null) { frameToProcess = _latestFrame; _latestFrame = null; } } if (frameToProcess == null) return; // 没有新帧就直接返回 // 用Task.Run把直方图计算放到后台线程,不卡UI Task.Run(() => { // 这里写你的直方图计算逻辑,比如从frameToProcess提取像素值统计 int[] histogramData = CalculateHistogram(frameToProcess); // 计算完必须回到UI线程更新图表(WinForms控件只能在UI线程操作) this.Invoke((MethodInvoker)delegate { // 替换成你实际更新图表的代码,比如设置Series数据、刷新控件 UpdateHistogramChart(histogramData); }); // 记得释放帧资源,避免内存泄漏 frameToProcess.Dispose(); }); }
额外注意事项
- 帧数据复制:一定要确保你处理的是帧的副本,而不是相机正在使用的原对象,否则相机回收/复用帧时会导致数据错乱。如果
Frame没有Clone方法,就手动把像素数据复制到一个独立的字节数组或Bitmap里。 - 资源释放:用完
Frame后一定要调用Dispose,不然会内存泄漏,长时间运行程序会越来越卡。 - UI线程更新:WinForms的所有控件操作必须在UI线程,所以计算完直方图后一定要用
Invoke或BeginInvoke回到UI线程再更新图表。
这样修改后,Timer和相机回调就不会再抢着访问同一份数据了,直方图更新会稳定很多~
内容的提问来源于stack exchange,提问作者Trion
相关产品推荐
相关产品推荐

