Чтение и запись из очереди
Я сохраняю набор из ~300 растровых изображений в параллельной очереди. Я делаю это для программы потокового видео через 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
Там нет ошибки. Я думаю, что могут быть копии в очереди (потому что окно изображения, кажется, зависает)? Я попробовал тот же код с целыми числами, и он работает отлично. В чем проблема?
1 ответ
Сначала включи Option Strict
, Во-вторых, вы не должны получать доступ к элементам управления пользовательского интерфейса из другого потока. Основная проблема заключается в том, что вы на самом деле не помещаете более 300 различных изображений в очередь. Скорее, код перерисовывает следующее изображение к одному и тому же растровому объекту снова и снова. Вы также используете потенциально устаревший графический объект.
Некоторые другие вещи могут быть артефактами попытки заставить его работать, но нет причины клонировать изображение для отображения - это просто приводит к еще одной вещи, которую нужно утилизировать.
Это использует тот же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
метод. С небольшой задержкой я мог получить исключения "Объект используется в другом месте".
Изменение в отчете об отладке делает более понятным, что происходит:
' 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
Второе число правильное, но вы можете видеть, что 14-е и 15-еобъекты изображения были перезаписаны изображением 16. Когда писатель заканчивает работу, у вас остается много копий последнего загруженного изображения.
Исправлено с помощью тега, используемого для маркировки предметного индекса и отчетности, выполненной вReader
метод - когда они выходят:
' 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, так что, похоже, он не протекает.