2016-08-02 64 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傳遞的。它應該與我寫的代碼一起工作。 –