异步/等待仍在阻止UI?

时间:2019-11-22 03:03:54

标签: multithreading async-await task

对Async / Await不太熟悉,但是有此代码段,并且在调用UI时仍会阻塞UI?我只得到“等待”光标,无法移动窗口,等等。

    Public Async Function IsPortReachable(ByVal strHost As String, Optional ByVal intPort As Integer = 80, Optional ByVal intTimeoutMs As Integer = 5000) As Task(Of Boolean)

        ' Throw an exception if no host was passed
        If String.IsNullOrEmpty(strHost) Then Throw New ArgumentNullException(NameOf(strHost))

        Return Await Task.Run(Function()

                                  Dim clientDone As ManualResetEvent = New ManualResetEvent(False)
                                  Dim bReachable As Boolean = False
                                  Dim hostEntry As DnsEndPoint = New DnsEndPoint(strHost, intPort)

                                  Using socket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

                                      Dim socketEventArg = New SocketAsyncEventArgs With {.RemoteEndPoint = hostEntry}

                                      AddHandler socketEventArg.Completed, Sub(s, e)
                                                                               bReachable = e.SocketError = SocketError.Success
                                                                               clientDone.Set()
                                                                           End Sub

                                      clientDone.Reset()
                                      socket.ConnectAsync(socketEventArg)
                                      clientDone.WaitOne(intTimeoutMs)

                                      Return bReachable

                                  End Using

                              End Function).ConfigureAwait(False)
    End Function

我从这样的主要形式调用它,它也发生在Form_Load上,因此在用户能够“收回控制权”之前会有一个不必要的延迟:

Dim bTask As Task(Of Boolean) = IsPortReachable(dicValidated.Item("DNS2Up"), Convert.ToInt32(dicValidated.Item("DNS2UpPort")), 1000)

给我的印象是Async / Await函数在另一个线程上运行,而使调用线程(UI线程)不受阻塞?

1 个答案:

答案 0 :(得分:0)

调用异步方法不会启动或使用其他线程,也不会使该方法的执行异步。仅在该方法中第一次发生await操作时,才会将执行返回给调用者,并连接异步继续以稍后异步地运行该方法的其余部分(有一个例外,但现在请使其保持简单)。 / p>

但是,即使继续执行也可以在调用线程上运行,尤其是在涉及到同步上下文的情况下,例如在使用WinForms或WPF的情况下。它可能导致死锁,但是您已经通过添加ConfigureAwait(false)解决了它。

如果我理解正确(并且代码表明相同),则代码中没有死锁,您只会在UI中遇到“短暂的”无响应。我怀疑您稍后在bTask.Result函数中调用Form_Load,这会阻塞UI线程,直到IsPortReachable完成其工作。将此阻止代码更改为await bTask应该可以解决您的问题。

旁注:我还将避免使用ManualResetEvent并使用await来调用socket.ConnectAsync()。这种情况下,您也可以摆脱Task.Run(...)