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

エラーはありません。キューにコピーがあると思います (画像ボックスがフリーズしているように見えるため)。整数で同じコードを試してみましたが、完全に機能します。どうしたの?

4

1 に答える 1

1

まず、 をオンにしOption Strictます。次に、別のスレッドから UI コントロールにアクセスしないでください。核となる問題は、キューに 300 以上の異なる画像を入れていないことです。代わりに、コードは次のイメージを同じBitmap オブジェクトに何度も再描画します。古い可能性のあるグラフィックス オブジェクトも使用しています。

他のいくつかのものは、それを機能させようとする成果物である可能性がありますが、表示のために画像を複製する理由はありません。処分するものがもう 1 つ増えるだけです。

これは、同じ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 として

2 番目の数値は正しいですが、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オブジェクトの変更がまったく見られなかったので、リークしていないようです。

于 2016-08-02T21:39:46.820 に答える