locked
GetRequestStreamAsync blocking

    Question

  • Hi,

    most likely I'm doing something completely wrong. So please be patient and just assume that I'm not the brightest light around ;)

    I tried to create my first "usefull" app using the new async extensions: Reading some stuff out of a website to automate some tasks. See code below.

    I had a Progressbar in Marquee Mode on my form and so I immediatley noticed that the UI thread was blocked for some time, when I called my function. I assumed that I had some blocking synchronous call inside so I used the performance wizard to show me where to start searching. I was surprised that it was GetRequestStreamAsync that blocked for around 10-20 seconds.

    Can somebody explain this behavior?

     

    Code:

     

    Private Async Function Login() As Tasks.Task(Of Boolean)
    
      Dim wr As HttpWebRequest = HttpWebRequest.Create("https://xxxxxxx/login.php")
      Dim result As String = String.Empty
      With wr
    
       ' set up the WR
       .CookieContainer = Cookies
       .Method = "POST"
       .ContentType = "application/x-www-form-urlencoded"
       .Referer = "https://xxxxxxx/index.php"
       .AllowAutoRedirect = True
       Dim b() As Byte = System.Text.Encoding.ASCII.GetBytes("login_pass_inputbox=" & Me.Password &
                     "&login_user_inputbox=" & Me.UserID)
       .ContentLength = b.Count
    
       ' Here ist the blocking part - just WHY?
       Dim RS As System.IO.Stream = Await .GetRequestStreamAsync
       With RS
        Await .WriteAsync(b, 0, b.Count)
        .Flush()
        .Close()
       End With
    
       ' call server to get a response
       With Await .GetResponseAsync()
        With New IO.StreamReader(.GetResponseStream)
         result = Await .ReadToEndAsync()
        End With
       End With
    
      End With
    
      If result.IndexOf("xxxxxxxx", System.StringComparison.InvariantCultureIgnoreCase) < 0 Then Return False
    
      Return True
    
     End Function
    

     

    EDIT: Of course, I must be stupid ;)

    The calling function

    Public Async Function ReadAsync(ByVal Progress As ProgressDelegate) As Tasks.Task(Of Boolean)
    
        Cookies = New CookieContainer
        Dim t As New Tasks.TaskFactory(Of Boolean)
        If Progress IsNot Nothing Then Progress("Login to KonsoleH", 10)
        ' First version
        'If Not Await Login() Then Return False
        ' Second version: non blocking
        If Not Await Tasks.TaskEx.RunEx(AddressOf Login) Then Return False
        If Progress IsNot Nothing Then Progress("Reading Domains", 20)
        Return True
    
      End Function

    The second version works without blocking the UI thread. Though I thought that async was meant to be a fire-and-forget solution for dummies? I did not expect that there are "async" functions that still block for some reason?


    Wednesday, July 27, 2011 12:26 PM

Answers

  • Hi Picoflop-

    The Async CTP's GetRequestStreamAsync and GetResponseAsync are simple wrappers around the existing HttpWebRequest.BeginGetRequestStream and BeginGetResponse in .NET 4.  Those Begin* methods have a lot of setup work they do (e.g. proxy, DNS, connection pooling, etc.) before they can submit a request, and unfortunately today that work all happens synchronously as part of the Begin* call.  If there's something in your environment that could be delaying one of those synchronous activities, that could be causing the visible delay you've noticed.  The workaround for now, as you've discovered, is to wrap such slow invocations in another task that will move that synchronous work off to the thread pool so that it doesn't block your UI thread.

    Thanks.

    Saturday, July 30, 2011 12:50 AM
    Moderator
  • Yes HttpWebRequest has a lot of setup work to do, but there is something wrong with the machine/environment if that setup work is taking multiple seconds. The most likely candidates are DNS and proxies. You can try the following to help narrow it down:

    1. Measure how long System.Net.Dns.GetHostEntry("destinationServer") takes.

    2. Measure how long System.Net.WebRequest.DefaultWebProxy.GetProxy(new Uri("http://destinationServer")) takes.

    3. Enable System.Net tracing with timestamps to see at what point the request gets stuck for so long. See http://msdn.microsoft.com/en-us/library/ty48b824.aspx

    Monday, August 01, 2011 10:43 PM

All replies

  • Hi Picoflop-

    The Async CTP's GetRequestStreamAsync and GetResponseAsync are simple wrappers around the existing HttpWebRequest.BeginGetRequestStream and BeginGetResponse in .NET 4.  Those Begin* methods have a lot of setup work they do (e.g. proxy, DNS, connection pooling, etc.) before they can submit a request, and unfortunately today that work all happens synchronously as part of the Begin* call.  If there's something in your environment that could be delaying one of those synchronous activities, that could be causing the visible delay you've noticed.  The workaround for now, as you've discovered, is to wrap such slow invocations in another task that will move that synchronous work off to the thread pool so that it doesn't block your UI thread.

    Thanks.

    Saturday, July 30, 2011 12:50 AM
    Moderator
  • Yes HttpWebRequest has a lot of setup work to do, but there is something wrong with the machine/environment if that setup work is taking multiple seconds. The most likely candidates are DNS and proxies. You can try the following to help narrow it down:

    1. Measure how long System.Net.Dns.GetHostEntry("destinationServer") takes.

    2. Measure how long System.Net.WebRequest.DefaultWebProxy.GetProxy(new Uri("http://destinationServer")) takes.

    3. Enable System.Net tracing with timestamps to see at what point the request gets stuck for so long. See http://msdn.microsoft.com/en-us/library/ty48b824.aspx

    Monday, August 01, 2011 10:43 PM
  • Stephen, thanks for the answer. Though I still think, it might not be the best idea to mark something with "Async" if it still can block for some time. Maybe something like "FunctionNamePartlyAsync" fits better ;)

    I mean: If I SEE a function name decorated with "Async", I (and probably not only me) usually expect that the call returns asap. Isn't that simply the basic idea behind all that async stuff?

     

    Tratcher: Maybe there is something "wrong" with my network setup - but it still works ;)

    Wednesday, August 03, 2011 3:44 PM
  • Stephen -

    Are there plans to make BeginGetRequestStream/BeginGetResponse truly asynchronous? If not, would Async CTP consider RunEx wrappers instead of FromAsync wrappers for these?

    IMO, if an operation is slow because of CPU usage, then it's the end user's job to use RunEx. But if an operation is slow because of I/O, then the framework should provide a truly asynchronous *Async method.

           -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
      and How to Implement IDisposable and Finalizers: 3 Easy Rules
    Microsoft Certified Professional Developer

    How to get to Heaven according to the Bible
    Thursday, August 04, 2011 6:42 PM
  • No, there are not currently plans to re-write BeginGetRequestStream or BeginGetResponse.  We are investigating alternatives for the Task versions of these APIs.
    Thursday, August 04, 2011 8:22 PM