从队列中读取和写入

时间:2016-08-02 18:17:05

标签: .net vb.net multithreading bitmap concurrent-queue

我在并发队列中保存了一组~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

没有错误。我认为队列中可能有副本(因为图片框似乎冻结了)?我用整数尝试了相同的代码,它完美无缺。问题是什么?

1 个答案:

答案 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对象的变化,所以它似乎没有泄漏。