VB.NETでFIFOにバックグラウンド処理をする

Queue(Of [Delegate])でとっても簡単だったというお話です。。。


パフォーマンスチューニングの際に、あわせてオブジェクトサイズを計測したりしますよね?

BinaryFormatter.Serializeでシリアライズして、MemoryStream.Lengthでバイト長を求めるのですが、オブジェクトが巨大だと結構ハイコスト*1なのでみんなとっても困っているはずです。

こんなふうに使い捨てスレッドでやってもいいのですが、処理順がめちゃめちゃになってみんなとっても困っているはずです。

Dim t As New Threading.Thread(
    New Threading.ThreadStart(
        Sub()
            ' ここでオブジェクトサイズ計測など。。。
        End Sub))
t.Start()

と、いう事で、唯一のバックグランドスレッドを設け、直列化させて仕事させましょう。


ポイント

  • 実装はQueue(Of [Delegate])で使えば簡単。
  • 使う方はlambda式で簡単。

BackgroundInvokerの実装

Option Explicit On
Option Strict On

Public Class BackgroundInvoker
    Private Shared _queue As New Queue(Of [Delegate])

    Shared Sub New()
        Dim t As New Threading.Thread(
            New Threading.ThreadStart(
                Sub()
                    Do
                        Dim d As [Delegate] = Nothing
                        SyncLock _queue
                            If 0 < _queue.Count Then d = _queue.Dequeue()
                        End SyncLock
                        If d Is Nothing Then
                            Threading.Thread.Sleep(20)
                            Continue Do
                        End If
                        d.DynamicInvoke()
                    Loop
                End Sub))
        t.IsBackground = True   ' メインスレッド終了時にこのスレッドも終了するように。
        t.Start()
    End Sub

    Public Shared Sub Request(ByVal d As [Delegate])
        SyncLock _queue
            _queue.Enqueue(d)
        End SyncLock
    End Sub
End Class

Delegate型をEnqueueするメソッドがあるのと、バックグランドスレッドがDequeueしてDynamicInvokeしているだけなのでシンプルですね。*2


使用例

オブジェクトのサイズを計測してDebug.Print的な例です。

BackgroundInvoker.Request(Sub() Debug.Print("Hogeのサイズは" & ObjectUtil.SizeOf(hoge).ToString))

lambdaで委譲するだけなのでこれまたシンプルですね。


おまけ

オブジェクト*3のサイズを計測する例です。

Option Explicit On
Option Strict On

Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary

Public Class ObjectUtil
    Private Shared _memoryStream As New MemoryStream()
    Private Shared _binaryWriter As New BinaryWriter(_memoryStream)
    Private Shared _binaryFormatter As New BinaryFormatter()

    Public Shared Function SizeOf(Byval obj As Object) As Long
        If obj Is Nothing Then Return 0

        SyncLock _binaryFormatter
            _memoryStream.SetLength(0)
            _binaryFormatter.Serialize(_binaryWriter.BaseStream, obj)
            _binaryWriter.Flush()
            Return _memoryStream.Length
        End SyncLock
    End Function
End Class


Work! Enjoy it!

*1:ミイラ取りが。。的な。

*2:ガチで使う時は例外処理を真面目に書きましょう。

*3:シリアライズ可能なもの限定ですが。