VB.Net BeginGetResponse比使用Await GetResponseAsync快得多?

时间:2016-08-09 00:37:09

标签: vb.net visual-studio

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

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

然而,使用Await GetResponseAsync需要5倍。大约3000ms。在制作中,在扩大规模时,这对我来说真的很重要。我做错了什么,或者Await 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

1 个答案:

答案 0 :(得分:2)

  

是否等待GetResponseAsync本身比BeginGetResponse慢?

如果没有良好的Minimal, Complete, and Verifiable code example,很难解决您的具体表现问题。那说......

在我看来,你在这里比较苹果和橘子。首先,实施中存在重大差异。在BeginGetResponse()版本中,您同时启动所有请求,因此假设Web服务器会容忍它,它们会并行完成。在GetResponseAsync()版本中,您只需在上一个请求完成后发起新请求。

这种序列化必然会减慢一切。

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

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

当你使用它时,发现GetResponseAsync()方法速度较慢,我一点都不会感到惊讶。

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

所有这一切,当我更改你的代码以便同时运行GetResponseAsync()版本时,我发现至少在我测试过的网络服务器上,它的工作速度与{{1}一样快}版本。在两种情况下,它都能在10秒内完成100次迭代。

BeginGetResponse()

有可能使用速度更快的Web服务器,您可能会开始遇到UI线程作为瓶颈问题,但我会说主要区别可能只是两个实现在逻辑上甚至不是相同。