2016-08-02 66 views
1

我正在将一组〜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

您有可能得到未报告的异常。由于它看起来只有3个物体被丢弃,所以当你耗尽物体时,可能会出现可怕的通用GDI错误或其他一些错误 – Plutonix

+0

你是什么意思?我只处理图片boc图像和一个位图(“位”)。你是指那些? –

回答

1

首先,打开Option Strict。其次,你不应该从另一个线程访问UI控件。核心问题是,你不是真的把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;然后通过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

第二个数字是正确的,但你可以看到,第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对象的变化,所以它看起来没有泄漏。

+0

但是一次又一次使用相同的crop_bit有什么问题? (使用你的方法,虽然公羊疯了)。我的意思是Enqueue方法使用了crop_bit的副本。它不是通过引用val传递的。它应该与我写的代码一起工作。 –