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()
と、いう事で、唯一のバックグランドスレッドを設け、直列化させて仕事させましょう。
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!