問題は、発信者がオブジェクトを破棄するのを忘れた場合にどうするかです。IDisposable
したがって、簡潔にするために実装をスキップしました。私の質問は次のとおりです:最後のコード サンプルはどのくらい危険ですか? 改善できますか?それとも、エレガンスに執着しすぎているのでしょうか?それとも、これをより適切に行うための別のパターンが既にあるのでしょうか?
ストーリーは、長時間実行されるプロセスをラップして、メイン スレッドから安全にアクセスできるようにすることから始まります。
Public Class ThisCantRaiseEvents
Private client As UdpClient
Public Sub New(ByVal client As UdpClient)
Me.client = client
Dim thread As New Thread(AddressOf DoWork)
thread.IsBackground = True
thread.Start(client)
End Sub
Public Event PacketReceived As EventHandler
Protected Overrides Sub Finalize()
client.Close()
End Sub
Private Shared Sub DoWork(ByVal state As Object)
Dim client As UdpClient = DirectCast(state, UdpClient)
Try
While True
client.Receive(Nothing)
End While
Catch ex As Exception
End Try
End Sub
End Class
イベントをメイン スレッドにディスパッチする素朴な試み。うまく機能しますが、独自のスレッドがそれを維持するため、範囲外になることはありません。(Thread オブジェクトがスコープ外に出ても、スレッドは停止しないことに注意してください。)
Public Class ThisWillLeak
Private client As UdpClient
Private mainThread As Control
Public Sub New(ByVal client As UdpClient, ByVal control As Control)
Me.client = client
Me.mainThread = control
Dim thread As New Thread(AddressOf DoWork)
thread.IsBackground = True
thread.Start()
End Sub
Public Event PacketReceived As EventHandler
Protected Overrides Sub Finalize()
client.Close()
End Sub
Protected Overridable Sub OnPacketReceived(ByVal e As EventArgs)
RaiseEvent PacketReceived(Me, e)
End Sub
' The worker thread has a pointer to ThisWillLeak
' and so it will live forever!
Private Sub DoWork(ByVal state As Object)
Try
While True
client.Receive(Nothing)
mainThread.BeginInvoke(New Action(Of EventArgs) _
(AddressOf OnPacketReceived), EventArgs.Empty)
End While
Catch ex As Exception
End Try
End Sub
End Class
' これは、前のサンプルの問題を解決する最初の試みでした。残念ながら、DoWork メソッドがより複雑になるにつれて、非常に見苦しくなります。
Public Class ThisIsVeryUgly
Private client As UdpClient
Private mainThread As Control
Public Sub New(ByVal client As UdpClient, ByVal control As Control)
Me.client = client
Me.mainThread = control
Dim thread As New Thread(AddressOf DoWork)
thread.IsBackground = True
thread.Start(New WeakReference(Me))
End Sub
Public Event PacketReceived As EventHandler
Protected Overrides Sub Finalize()
client.Close()
End Sub
Protected Overridable Sub OnPacketReceived(ByVal e As EventArgs)
RaiseEvent PacketReceived(Me, e)
End Sub
Private Shared Sub DoWork(ByVal state As Object)
Dim weakThis As WeakReference = DirectCast(state, WeakReference)
Dim this As ThisIsVeryUgly = DirectCast(weakThis.Target, ThisIsVeryUgly)
Dim client As UdpClient = this.client
this = Nothing
Try
While True
client.Receive(Nothing)
this = DirectCast(weakThis.Target, ThisIsVeryUgly)
this.mainThread.BeginInvoke(New Action(Of EventArgs) _
(AddressOf this.OnPacketReceived), EventArgs.Empty)
this = Nothing
' Repeat for every other place we need to raise an event! Ugh!
End While
Catch ex As Exception
End Try
End Sub
End Class
これは私の最新の発明です。これは洗練されていsender
ますが、発生するイベントの引数がユーザーが期待するものではないため、問題を引き起こす可能性があります。これにより問題が発生する可能性はありますか?
Public Class ThisIsAlmostGood
Private client As UdpClient
Private worker As WorkThread
Public Sub New(ByVal client As UdpClient, ByVal mainThread As Control)
Me.client = client
worker = New WorkThread(client, mainThread)
End Sub
Public Custom Event PacketReceived As EventHandler
AddHandler(ByVal value As EventHandler)
AddHandler worker.PacketReceived
End AddHandler
RemoveHandler(ByVal value As EventHandler)
RemoveHandler worker.PacketReceived
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
End RaiseEvent
End Event
Protected Overrides Sub Finalize()
client.Close()
End Sub
Private Class WorkThread
Private client As UdpClient
Private mainThread As Control
Public Sub New(ByVal client As UdpClient, ByVal mainThread As Control)
Me.client = client
Me.mainThread = mainThread
Dim thread As New Thread(AddressOf DoWork)
thread.IsBackground = True
thread.Start()
End Sub
Public Event PacketReceived As EventHandler
Protected Overridable Sub OnPacketReceived(ByVal e As EventArgs)
' But the user expects a pointer to ThisIsAlmostGood,
' not WorkThread! Should I pass Nothing instead?
RaiseEvent PacketReceived(Me, e)
End Sub
Private Sub DoWork(ByVal state As Object)
Try
While True
client.Receive(Nothing)
mainThread.BeginInvoke(New Action(Of EventArgs) _
(AddressOf OnPacketReceived), EventArgs.Empty)
End While
Catch ex As Exception
End Try
End Sub
End Class
End Class