从队列中读取和写入
Reading and writing from queue
我正在并发队列中保存一组约 300 个位图。我这样做是为了一个 over-tcp 视频流程序。如果服务器速度变慢,我会将接收到的位图保存在此队列中(缓冲)。我创建了一个单独的项目来测试它,但我遇到了一些问题。
当写入线程正在工作(写入队列)时,图片框正在显示队列中的图像,但它似乎跳过了很多图像(就像它正在读取刚刚添加到 "list" 通过写入线程而不是 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
没有错误。我认为队列中可能有副本(因为图片框似乎冻结了)?我用整数尝试了相同的代码,并且效果很好。有什么问题?
首先,开启Option Strict
。其次,您不应该从另一个线程访问 UI 控件。核心问题是您并没有真正将 300 多张 不同的图像 放入队列中。相反,代码将下一张图像一遍又一遍地重新绘制到 same 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;然后 7 通过 Write
方法。短暂的延迟,我可以获得 "Object is in use elsewhere" 异常。
对调试报告的更改使正在发生的事情更加清楚:
' in "read"
Console.WriteLine("tag {0:00} as # {1:00}",
bit.Tag.ToString, rCount)
tag
是入队时分配给它的编号,rCount
是"Dequeue count"还是在队列中的位置:
tag 13 as # 04
tag 16 as # 05
tag 20 as # 06
tag 24 as # 07
tag 28 as # 08
第二个数字是正确的,但是您可以看到第 14 个和第 15 个 图像对象 被图像 16 覆盖了。当作者完成时,您会留下许多副本最后加载的图像。
修复了用于在 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 对象发生变化,因此它似乎没有泄漏。
我正在并发队列中保存一组约 300 个位图。我这样做是为了一个 over-tcp 视频流程序。如果服务器速度变慢,我会将接收到的位图保存在此队列中(缓冲)。我创建了一个单独的项目来测试它,但我遇到了一些问题。
当写入线程正在工作(写入队列)时,图片框正在显示队列中的图像,但它似乎跳过了很多图像(就像它正在读取刚刚添加到 "list" 通过写入线程而不是 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
没有错误。我认为队列中可能有副本(因为图片框似乎冻结了)?我用整数尝试了相同的代码,并且效果很好。有什么问题?
首先,开启Option Strict
。其次,您不应该从另一个线程访问 UI 控件。核心问题是您并没有真正将 300 多张 不同的图像 放入队列中。相反,代码将下一张图像一遍又一遍地重新绘制到 same 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;然后 7 通过 Write
方法。短暂的延迟,我可以获得 "Object is in use elsewhere" 异常。
对调试报告的更改使正在发生的事情更加清楚:
' in "read"
Console.WriteLine("tag {0:00} as # {1:00}",
bit.Tag.ToString, rCount)
tag
是入队时分配给它的编号,rCount
是"Dequeue count"还是在队列中的位置:
tag 13 as # 04
tag 16 as # 05
tag 20 as # 06
tag 24 as # 07
tag 28 as # 08
第二个数字是正确的,但是您可以看到第 14 个和第 15 个 图像对象 被图像 16 覆盖了。当作者完成时,您会留下许多副本最后加载的图像。
修复了用于在 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 对象发生变化,因此它似乎没有泄漏。