VB.Net开始响应比使用Await GetResponseAsync快得多?

VB.Net开始响应比使用Await GetResponseAsync快得多?,vb.net,visual-studio,Vb.net,Visual Studio,我试图开始使用任务,但我想比较使用标准HttpWebRequest.BeginGetResponse时的速度差异 据我所知,使用BeginGetResponse向example.com发送和完成100个请求需要大约600毫秒 然而,使用Await GetResponseAsync需要5倍的时间。大约3000毫秒。在生产中,当规模扩大时,这对我来说真的很重要。是我做错了什么,还是等待GetResponseAsync天生比开始响应慢 Imports System.Net Public Class

我试图开始使用任务,但我想比较使用标准HttpWebRequest.BeginGetResponse时的速度差异

据我所知,使用BeginGetResponse向example.com发送和完成100个请求需要大约600毫秒

然而,使用Await GetResponseAsync需要5倍的时间。大约3000毫秒。在生产中,当规模扩大时,这对我来说真的很重要。是我做错了什么,还是等待GetResponseAsync天生比开始响应慢

Imports System.Net

Public Class Form1
    Private sw As New Stopwatch
    Private respCounter As Integer
    Private iterations As Integer = 100

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        sw.Start()

        For i = 1 To iterations
            Dim req As HttpWebRequest = HttpWebRequest.Create("http://example.com")
            Dim state As New RequestState
            state.req = req
            req.BeginGetResponse(AddressOf respCallback, state)
        Next
    End Sub

    Private Sub respCallback(ar As IAsyncResult)
        Dim state As RequestState = ar.AsyncState
        state.resp = state.req.EndGetResponse(ar)
        state.respStream = state.resp.GetResponseStream
        state.respStream.BeginRead(state.buffer, 0, 1024, AddressOf readCallback, state)
    End Sub

    Private Sub readCallback(ar As IAsyncResult)
        Dim state As RequestState = ar.AsyncState
        Dim read As Integer = state.respStream.EndRead(ar)
        If read > 0 Then
            state.respBody += System.Text.ASCIIEncoding.ASCII.GetString(state.buffer, 0, read)
            state.respStream.BeginRead(state.buffer, 0, 1024, AddressOf readCallback, state)
        Else
            state.Dispose()
            respCounter += 1
            If respCounter = iterations Then
                respCounter = 0
                sw.Stop()
                Debug.WriteLine(sw.ElapsedMilliseconds)
                sw.Reset()
            End If
        End If
    End Sub

    Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        sw.Start()

        For i = 1 To iterations
            Dim req As HttpWebRequest = HttpWebRequest.Create("http://example.com")
            Using resp As WebResponse = Await req.GetResponseAsync
                Using sr As New IO.StreamReader(resp.GetResponseStream)
                    Dim respBody As String = Await sr.ReadToEndAsync
                End Using
            End Using
            respCounter += 1
            If respCounter = iterations Then
                respCounter = 0
                sw.Stop()
                Debug.WriteLine(sw.ElapsedMilliseconds)
                sw.Reset()
            End If
        Next

        MsgBox("Execution!")
    End Sub

End Class

Public Class RequestState
    Implements IDisposable

    Public req As HttpWebRequest
    Public resp As HttpWebResponse
    Public respStream As IO.Stream
    Public buffer(1024) As Byte
    Public respBody As String

#Region "IDisposable Support"
    Private disposedValue As Boolean ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not disposedValue Then
            If disposing Then
                ' TODO: dispose managed state (managed objects).
                respStream.Close()
                respStream.Dispose()
                resp.Close()
            End If

            ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
            ' TODO: set large fields to null.
        End If
        disposedValue = True
    End Sub

    ' TODO: override Finalize() only if Dispose(disposing As Boolean) above has code to free unmanaged resources.
    'Protected Overrides Sub Finalize()
    '    ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
    '    Dispose(False)
    '    MyBase.Finalize()
    'End Sub

    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
        Dispose(True)
        ' TODO: uncomment the following line if Finalize() is overridden above.
        ' GC.SuppressFinalize(Me)
    End Sub
#End Region
End Class
Wait GetResponseAsync是否天生比BeginGetResponse慢

Imports System.Net

Public Class Form1
    Private sw As New Stopwatch
    Private respCounter As Integer
    Private iterations As Integer = 100

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        sw.Start()

        For i = 1 To iterations
            Dim req As HttpWebRequest = HttpWebRequest.Create("http://example.com")
            Dim state As New RequestState
            state.req = req
            req.BeginGetResponse(AddressOf respCallback, state)
        Next
    End Sub

    Private Sub respCallback(ar As IAsyncResult)
        Dim state As RequestState = ar.AsyncState
        state.resp = state.req.EndGetResponse(ar)
        state.respStream = state.resp.GetResponseStream
        state.respStream.BeginRead(state.buffer, 0, 1024, AddressOf readCallback, state)
    End Sub

    Private Sub readCallback(ar As IAsyncResult)
        Dim state As RequestState = ar.AsyncState
        Dim read As Integer = state.respStream.EndRead(ar)
        If read > 0 Then
            state.respBody += System.Text.ASCIIEncoding.ASCII.GetString(state.buffer, 0, read)
            state.respStream.BeginRead(state.buffer, 0, 1024, AddressOf readCallback, state)
        Else
            state.Dispose()
            respCounter += 1
            If respCounter = iterations Then
                respCounter = 0
                sw.Stop()
                Debug.WriteLine(sw.ElapsedMilliseconds)
                sw.Reset()
            End If
        End If
    End Sub

    Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        sw.Start()

        For i = 1 To iterations
            Dim req As HttpWebRequest = HttpWebRequest.Create("http://example.com")
            Using resp As WebResponse = Await req.GetResponseAsync
                Using sr As New IO.StreamReader(resp.GetResponseStream)
                    Dim respBody As String = Await sr.ReadToEndAsync
                End Using
            End Using
            respCounter += 1
            If respCounter = iterations Then
                respCounter = 0
                sw.Stop()
                Debug.WriteLine(sw.ElapsedMilliseconds)
                sw.Reset()
            End If
        Next

        MsgBox("Execution!")
    End Sub

End Class

Public Class RequestState
    Implements IDisposable

    Public req As HttpWebRequest
    Public resp As HttpWebResponse
    Public respStream As IO.Stream
    Public buffer(1024) As Byte
    Public respBody As String

#Region "IDisposable Support"
    Private disposedValue As Boolean ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not disposedValue Then
            If disposing Then
                ' TODO: dispose managed state (managed objects).
                respStream.Close()
                respStream.Dispose()
                resp.Close()
            End If

            ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
            ' TODO: set large fields to null.
        End If
        disposedValue = True
    End Sub

    ' TODO: override Finalize() only if Dispose(disposing As Boolean) above has code to free unmanaged resources.
    'Protected Overrides Sub Finalize()
    '    ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
    '    Dispose(False)
    '    MyBase.Finalize()
    'End Sub

    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
        Dispose(True)
        ' TODO: uncomment the following line if Finalize() is overridden above.
        ' GC.SuppressFinalize(Me)
    End Sub
#End Region
End Class
如果没有良好的解决方案,很难解决您的具体性能问题。也就是说

我觉得你在比较苹果和桔子。首先也是最重要的一点是,在实现上有很大的不同。在您的
BeginGetResponse()
版本中,您可以同时启动所有请求,因此假设web服务器能够容忍,它们将并行完成。在
GetResponseAsync()
版本中,只有在前一个请求完成后才能启动新请求

这种序列化必然会降低速度

除此之外,
BeginGetResponse()
版本在IOCP线程池中执行其所有工作,而
GetResponseAsync()
版本使用单个UI线程来处理I/O事件的完成。UI线程是一个瓶颈,这既是因为它一次只能做一件事,也是因为您必须等待它从执行其他任务中可用,然后才能继续处理I/O完成问题(“一次只能做一件事”问题的变体)

除此之外,您还必须处理消息循环中涉及的延迟,该消息循环将异步完成排出队列,以便在UI线程中执行

如果我发现
GetResponseAsync()
方法在您使用它的方式中使用起来比较慢,我一点也不会感到惊讶

如果您想从中获得更好的性能,您可能应该在异步调用中使用
ConfigureAwait(false)
。当然,这假设您可以以其他方式最小化与UI线程的交互(例如,结果的处理实际上不需要直接发回UI线程)。但是这样做会告诉框架不要麻烦将完成封送回UI线程。至少在您发布的代码中,这是安全的,因为您实际上不与
async
事件处理程序方法中的UI对象交互

综上所述,当我更改了您的代码,使其能够同时运行
GetResponseAsync()
版本时,我发现至少在我测试的web服务器上,它的运行速度与
BeginGetResponse()
版本一样快。在这两种情况下,它都能在10秒多一点的时间内完成100次迭代

Private Async Sub Button2\u单击(发送方作为对象,e作为事件参数)处理按钮2。单击
sw.Start()
将任务作为列表(任务的列表(字符串的列表))=新列表(任务的列表(字符串的列表))
对于i=1到1次迭代
Dim req作为HttpWebRequest=HttpWebRequest.Create(“http://example.com/")
任务。添加(读取响应(req))
下一个
等待任务。何时(任务)
sw.Stop()
调试写入线(sw.ElapsedMilliseconds)
sw.Reset()
MsgBox(“执行!”)
端接头
专用异步函数ReadResponse(请求为HttpWebRequest)作为任务(字符串)
使用resp作为WebResponse=wait req.GetResponseAsync
使用sr作为新IO.StreamReader(分别为GetResponseStream)
Dim respBody As String=等待sr.ReadToEndAsync
返回响应体
终端使用
终端使用
端函数

使用更快的web服务器是可能的,您可能会开始遇到UI-thread-as-a-瓶颈问题,但我想说的主要区别可能是这两种实现在逻辑上根本不一样。

@x。。。您好,我在请求完成时添加了对EndGetResponseStream和EndGetResponse的调用。然后我得到一个神秘的异常“附加信息:IAsyncResult对象不是从这个类上相应的异步方法返回的。”我只是使用GetResponse()和GetResponseAsync()以及BeginGetResponse/EndGetResponse做了一些测试,它们同时显示。好的,感谢longish响应,我会把这个标记为答案,因为我很确定这就是我想要的解释。谢谢,有些事情我需要做一些额外的研究(是的,我是个书呆子)。我还将尝试一下ConfigureWait(false)提示。经过仔细检查,我注意到了一个我之前忽略的重要细节。请看我的最新答案。