Чтение и запись из очереди

Я сохраняю набор из ~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, так что, похоже, он не протекает.

Другие вопросы по тегам