2

Using块用于异步对象是否安全?如果在异步任务完成之前退出块,对象会不会被释放得太快?任务完成后会妥善处理吗?

这段代码有效,但我不知道它是否在滥用我的记忆。

For i = 1 To nPings
  Thread.Sleep(10)
  Using pPing As New System.Net.NetworkInformation.Ping
    AddHandler pPing.PingCompleted, AddressOf pingHandler
    pPing.SendAsync(ip, timeout)
  End Using
Next i
4

2 回答 2

3

如果在异步任务完成之前退出块,对象会不会被释放得太快?

是的。End Using线完成后,对象Ping将被释放。

任务完成后会妥善处理吗?

在您的示例中,该Ping对象将始终被丢弃。但是,这可能会在异步代码完成后发生,也可能不会发生,因为该行为(您编码它的方式)是未确定的。

要使此行为更具确定性,请在pingHandler代码中处理 Ping 对象,例如:

Class PingAsyncState

    Public Property Ping Ping
    Public Property int Timeout

End Class

For i = 1 To nPings
    Thread.Sleep(10)
    pPing As New System.Net.NetworkInformation.Ping
    AddHandler pPing.PingCompleted, AddressOf pingHandler
    pPing.SendAsync(ip, new PingAsyncState() With { .Ping = pPing, .Timeout = timeout })
Next i

Sub PingHandler(obj As Object)

    Dim state = CType(state, PingAsyncState)       

    ... code to do stuff ...

    state.Ping.Dispose()

End Sub

此外,如果您检查 内部的代码Ping.Dispose(),您会看到它关闭了各种 EventHandles。如果对象在异步完成之前被释放,这可能不会导致异常Ping,但它肯定会使Ping对象处于不稳定状态。您可能会发现未触发事件或未调用异步完整处理程序代码或发生其他微妙的事情。

于 2013-01-07T03:16:52.077 回答
2

我相信还有其他人可以为您提供底层 .Net 框架行为的详细解释,但结果是,通过附加事件处理程序,即使在处理该对象后,该对象仍会触发该事件。

您可以通过以下修改来证明这一点:

    ' Add any initialization after the InitializeComponent() call.
    For i = 1 To 4
        System.Threading.Thread.Sleep(10)
        Using pPing As New System.Net.NetworkInformation.Ping
            AddHandler pPing.PingCompleted, AddressOf pingHandler
            AddHandler pPing.Disposed, AddressOf pingDisposed
            pPing.SendAsync("someaddressthatmayormaynotwork.com", 10000)
        End Using
    Next i
    GC.Collect()
    Console.WriteLine("completed")


Public Sub pingHandler(sender As Object, e As System.Net.NetworkInformation.PingCompletedEventArgs)
    Console.WriteLine("pingCompleted")
End Sub
Public Sub pingDisposed(sender As Object, e As System.EventArgs)
    Console.WriteLine("Disposed")
End Sub

在这种情况下,如果定位 URL 或接收响应的时间过长,则 dispose 事件可以在 pingHandler 事件触发之前触发,但 pingHandler 事件将始终触发。

一般来说,最好的做法是在事件中释放异步事件处理程序,这样您就不会遇到像这样的范围/关闭场景:

Public Sub pingHandler(sender As Object, e As System.Net.NetworkInformation.PingCompletedEventArgs)
    If sender IsNot Nothing Then
        RemoveHandler DirectCast(sender, System.Net.NetworkInformation.Ping).PingCompleted, AddressOf pingHandler
    End If
    Console.WriteLine("pingCompleted")
End Sub

Public Sub pingDisposed(sender As Object, e As System.EventArgs)
    If sender IsNot Nothing Then
        RemoveHandler DirectCast(sender, System.Net.NetworkInformation.Ping).Disposed, AddressOf pingDisposed
    End If
    Console.WriteLine("Disposed")
End Sub
于 2013-01-07T03:12:59.800 回答