我在并发队列中保存了一组~300位图。我正在为一个over-tcp视频流程序做这件事。如果服务器速度变慢,我将收到的位图保存在此队列中(缓冲)。我创建了一个单独的项目来测试它,但我遇到了一些问题。
当写线程正在工作(写入队列)时,图片框显示队列中的图像,但似乎它跳过了许多图像(就像它正在读取刚刚添加的图片一样) "列出"通过写入线程 - 而不是FIFO行为)。当写入线程完成它禁止的图片框时,虽然我从队列中读取的循环仍在工作(当图片框阻止队列不为空时)。
以下是代码:
Imports System
Imports System.Drawing
Imports System.IO
Imports System.Threading
Imports System.Collections.Concurrent
Public Class Form1
Dim writeth As New Thread(AddressOf write), readth As New Thread(AddressOf read)
Dim que As New ConcurrentQueue(Of Bitmap), finished As Boolean
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'Start button
writeth.Start()
readth.Start()
End Sub
Sub draw(ByRef pic As Bitmap)
If PictureBox1.Image IsNot Nothing Then
PictureBox1.Image.Dispose()
PictureBox1.Image = Nothing
End If
PictureBox1.Image = pic
End Sub
Sub read()
Dim bit As Bitmap
While (Not finished Or Not que.IsEmpty)
If que.TryDequeue(bit) Then
draw(bit.Clone)
'Still working after the writing stopped
If finished Then Debug.Print("picture:" & que.Count)
Thread.Sleep(2000) 'Simulates the slow-down of the server
End If
End While
End Sub
Sub write()
Dim count As Integer = 0
Dim crop_bit As New Bitmap(320, 240), bit As Bitmap
Dim g As Graphics = Graphics.FromImage(crop_bit)
For Each fil As String In Directory.GetFiles(Application.StartupPath & "/pictures")
count += 1
Debug.Print(count)
bit = Image.FromFile(fil)
g.DrawImage(bit, 0, 0, 320, 240)
que.Enqueue(crop_bit)
bit.Dispose()
Next
finished = True
'At this point the picture box freezes but the reading loop still works
End Sub
End Class
没有错误。我认为队列中可能有副本(因为图片框似乎冻结了)?我用整数尝试了相同的代码,它完美无缺。问题是什么?
答案 0 :(得分:1)
首先,启用Option Strict
。其次,您不应该从另一个线程访问UI控件。核心问题是你并没有真正在阙中放置300多个不同的图像。相反,代码反复将下一个图像重绘为相同的 Bitmap对象。您还使用了可能过时的图形对象。
其他一些东西可能是尝试让它工作的工件,但是没有理由克隆图像以进行显示 - 它只会导致处理一件事。
这一遍使用相同的crop_bit
图片。
Sub write()
Dim count As Integer = 0
Dim crop_bit As New Bitmap(320, 240), bit As Bitmap
Dim g As Graphics = Graphics.FromImage(crop_bit)
...
que.Enqueue(crop_bit)
使用相同的crop_bit
表示Read
方法处理que(4)
时,它可能已更改为图像5;然后6;然后是Write
方法的7。短暂的延迟,我可以得到"对象在其他地方使用"例外。
对调试报告的更改使得它更加清晰:
' in "read"
Console.WriteLine("tag {0:00} as # {1:00}",
bit.Tag.ToString, rCount)
tag
是进入队列时分配给它的号码,rCount
是它"出队计数"或者它在队列中的位置:
标记13为#04
标记16为#05
标记20为#06
标记24为#07
将28标记为#08
第二个数字是正确的,但您可以看到图像16覆盖了第14个和第15个图像对象。当编写器完成时,您将保留最后一个图像的副本。
已修复用于标记项目索引和使用Reader
方法完成报告的标记 - 当它们 out 时
' for picture box display
Private DisplayImg As Action(Of Bitmap)
...
' initialize when you start the work:
DisplayImg = AddressOf Display
Sub Reader()
Dim bit As Bitmap = Nothing
Do
If que.TryDequeue(bit) Then
' do not acccess the UI from a different thread
' we know we are on a diff thread, just Invoke
pbImg.Invoke(DisplayImg, bit)
' report on the item
Console.WriteLine(bit.Tag.ToString)
Thread.Sleep(100) 'Simulates the slow-down of the server
End If
Loop Until (finished AndAlso que.IsEmpty)
End Sub
Sub Writer()
Dim count As Integer = 0
Dim crop_bit As Bitmap
' enumerate files is more efficient - loads one at a time
For Each fil As String In Directory.EnumerateFiles(filepath, "*.jpg")
count += 1
' need a NEW bitmap for each file
crop_bit = New Bitmap(320, 240)
' need to use and dispose of NEW graphics for each
' use a NEW img from file and dispose of it
Using g As Graphics = Graphics.FromImage(crop_bit),
img = Image.FromFile(fil)
g.DrawImage(img, 0, 0, 320, 240)
End Using
' put a collar on them
crop_bit.Tag = count.ToString
que.Enqueue(crop_bit)
Next
finished = True
End Sub
Sub Display(pic As Bitmap)
'... the same,
' handles the display AND disposal
...
End Sub
我作为测试运行了大约2000+,并且根本没有看到GDI对象的变化,所以它似乎没有泄漏。