Wrox Programmer Forums
Go Back   Wrox Programmer Forums > ASP.NET and ASP > ASP.NET 2.0 > ASP.NET 2.0 Professional
|
ASP.NET 2.0 Professional If you are an experienced ASP.NET programmer, this is the forum for your 2.0 questions. Please also see the Visual Web Developer 2005 forum.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the ASP.NET 2.0 Professional section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old May 25th, 2008, 07:40 PM
Registered User
 
Join Date: May 2008
Posts: 1
Thanks: 0
Thanked 0 Times in 0 Posts
Default Best Practice: Thread Management: Making Asynchron

Hello everyone,

I have a query that's probably quite common, but there's a part of how Threads are managed that I don't quite fully grasp, so i'm wondering what's the best way to solve my scenario.

The functional overview is quite simple: My site visitor wants to search, he enters a query, I forward that query to a 3rd party catalog that searches for me, and I return the results to the client.
The search works over AJAX, so I'm receiving the request on an ASHX handler, and returning the HTML that the JS will then put into a div directly.
This 3rd party service is expected to take a while to respond (many seconds)

My question is how to handle the threads on the server to avoid a bottleneck.
The obviously simplest way is to have the ASHX open a WebRequest to the 3rd party catalog directly. The obvious problem is, i can only have 25 queries running simultaneously in that case (i'm picking 25 as the number of threads in my pool from now on. I know this can be changed but that's not the point.)
I want to release these threads so they can keep handling other requests while the searches I spawned are pending, so the first thing I'm doing is using an IHttpAsyncHandler
In the method where I call the 3rd party URL, i'm using HttpWebRequest.BeginGetResponse (with a callback), so that part is handled Async too.

My question is...
When I call BeginGetResponse, I can immediately free the thread that I'm in and return it to the pool.
However, when I do that, am I not spawning a new thread that is alive while the request goes on, and ends up calling my callback once it finishes?

If I AM spawing this new thread, doesn't this consume one thread from the pool too, and I'm actually getting a net effect of the same as going synchronous?
If I am NOT spawning a new thread, then how does my callback get called? Who is checking whether this async method I started is finished? (This is the main part of the puzzle that I don't quite grasp)

If a response from the 3rd party catalog takes forever, could I potentially have 1000 of these async WebRequest methods pending a response at the same time, and still be using no threads (or very few) from the 25-thread pool?
Or will I only be able to have 25 of these waiting for a response from the catalog, and the rest will be queued up by IIS?


Below I'm attaching the code that I wrote that shows what i'm doing.
This is obviously oversimplified. I left the "architectural" stuff, and removed all the things specific to my case.

Am I doing things right? Should I be using an Async HTTP Handler, AND calling BeginGetResponse?
Is there a better way?


Thank you very much for your answers!

Daniel

 ----------------------------------------------------------------------------------------------

Code:
<%@ WebHandler Language="VB" Class="SearchHttpHandler" %>

Option Strict On

Imports System.Threading
Imports System.IO
Imports System.Net

Public Class SearchHttpHandler
    Implements IHttpAsyncHandler
    Implements IReadOnlySessionState

    '---------------------------------------------------------------------------------------

    Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
    End Sub


    '---------------------------------------------------------------------------------------

    Function BeginProcessRequest(ByVal context As System.Web.HttpContext, ByVal cb As System.AsyncCallback, ByVal extraData As Object) As System.IAsyncResult Implements IHttpAsyncHandler.BeginProcessRequest

        context.Response.Write("Started on thread: " & _
            Threading.Thread.CurrentThread.ManagedThreadId & " - " & _
            Threading.Thread.CurrentThread.IsThreadPoolThread.ToString & "<br />")

        'Instantiate the AsyncResult object that'll give us a hold to the context and the callback        
        Dim theAsyncResult As New SearchAsyncResult(context, cb, extraData)
        theAsyncResult.Search()
        Return theAsyncResult

    End Function

    '---------------------------------------------------------------------------------------

    Sub EndProcessRequest(ByVal result As System.IAsyncResult) Implements IHttpAsyncHandler.EndProcessRequest

        Dim theObj As SearchAsyncResult = DirectCast(result, SearchAsyncResult)
        theObj.Context.Response.Write("Ended on thread: " & _
            Threading.Thread.CurrentThread.ManagedThreadId & " - " & _
            Threading.Thread.CurrentThread.IsThreadPoolThread.ToString)

    End Sub

    '---------------------------------------------------------------------------------------

    Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

End Class

'======================================================================================
'======================================================================================

Public Class SearchAsyncResult
    Implements IAsyncResult

    Public Context As HttpContext

    Private mASPNETCallback As AsyncCallback
    Private mExtraData As Object
    Private mIsCompleted As Boolean
    Private mAsyncWaitHandle As ManualResetEvent
    Private mRequest As HttpWebRequest

    Private mQuery As String

    '-------------------------------------------------------------

    Public Sub New(ByVal context As HttpContext, ByVal ASPNETCallback As AsyncCallback, ByVal ExtraData As Object)
        Me.Context = context
        mASPNETCallback = ASPNETCallback
        mExtraData = ExtraData
    End Sub

    '-------------------------------------------------------------

    Public Sub Search()

        If Context.Request.QueryString("q") IsNot Nothing Then
            mQuery = Context.Request.QueryString("q")
        End If

        'Return "No Results" if there is no query
        If mQuery = "" Then
            Context.Response.Write("No Results")
            CompleteRequest()
            Exit Sub
        End If

        'Otherwise, make an async call
        Dim URL As String = "xxxxxxx?q=" & mQuery
        mRequest = DirectCast(WebRequest.Create(URL), HttpWebRequest)

        mRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
        mRequest.Method = "GET"

        mRequest.BeginGetResponse(AddressOf SearchCallback, Nothing)

    End Sub

    '-------------------------------------------------------------

    Private Sub SearchCallback(ByVal ar As IAsyncResult)

        Dim WebResp As WebResponse = mRequest.EndGetResponse(ar)
        Dim sReader As New StreamReader(WebResp.GetResponseStream())
        Dim result As String = sReader.ReadToEnd()

        Context.Response.Write(result)

        CompleteRequest()

    End Sub

    '-------------------------------------------------------------

    Private Sub CompleteRequest()

        mIsCompleted = True
        SyncLock Me
            If mAsyncWaitHandle IsNot Nothing Then
                mAsyncWaitHandle.Set()
            End If
        End SyncLock
        If mASPNETCallback IsNot Nothing Then
            mASPNETCallback(Me)
        End If

    End Sub

    '---------------------------------------------------------------------

#Region "Implementation of IAsyncResult dummy members"

    Public ReadOnly Property AsyncState() As Object Implements System.IAsyncResult.AsyncState
        Get
            Return mExtraData
        End Get
    End Property

    Public ReadOnly Property AsyncWaitHandle() As System.Threading.WaitHandle Implements System.IAsyncResult.AsyncWaitHandle
        Get

            SyncLock Me
                If mAsyncWaitHandle Is Nothing Then
                    mAsyncWaitHandle = New ManualResetEvent(False)
                End If
                Return mAsyncWaitHandle
            End SyncLock

        End Get
    End Property

    Public ReadOnly Property CompletedSynchronously() As Boolean Implements System.IAsyncResult.CompletedSynchronously
        Get
            Return False
        End Get
    End Property

    Public ReadOnly Property IsCompleted() As Boolean Implements System.IAsyncResult.IsCompleted
        Get
            Return mIsCompleted
        End Get
    End Property

#End Region

    '---------------------------------------------------------------------

End Class    'SearchAsyncResult
 
Old May 27th, 2008, 09:12 AM
planoie's Avatar
Friend of Wrox
 
Join Date: Aug 2003
Posts: 5,407
Thanks: 0
Thanked 16 Times in 16 Posts
Default

Where are you defining this 25 thread limit? Is it in IIS or is it part of the catalog search service?

I haven't worked with multi-threading in a web context enough to give an authoritative answer. But I have some thoughts that might help.

As long as something is happening (synchronous or asynchronous) some thread is handling it. If you spin off an asynchronous call to some process (the catalog search) then it's possible that you are actually consuming more threads. The call to the ASHX must be waiting on the async callback otherwise it wouldn't be useful. Therefore, you are just changing the "active" thread from the primary (ASHX handler thread) to the secondary (search thread). Keeping it synchronous may actually save a thread.

I recall reading an MSDN magazine article about asynchronous ASP.NET design. It was focused on spawning async calls to long running database process, much like yours, in order to keep the ASP.NET worker process threads free for handling other page requests. I don't remember how it works, and I'm still confused about how exactly the threads are really freed up. The ASP.NET page request much wait for the async call to complete otherwise it can't delivery HTML back. I don't recall any code that was written to explicitly manage the threads themselves apart from starting the async calls. I guess the ASP.NET runtime manages all of that using the standard threading management.

I imagine you will need to implement some pretty thorough load testing to test this architecture out. You might be able to throttle back the available threads such that you can manually test to see if the architecture works by forcing some bottlenecks and creating additional page requests.

-Peter
compiledthoughts.com





Similar Threads
Thread Thread Starter Forum Replies Last Post
abort Asynchron? ramo9941 C# 1 May 3rd, 2007 07:07 AM
An error for AJAX practice wen BOOK: Beginning Ajax with ASP.NET 0 December 13th, 2006 06:02 PM
practice help with C++ mastrgamr C++ Programming 2 November 2nd, 2006 07:55 AM
Looking for some practice Vano2005 C++ Programming 2 July 7th, 2005 04:30 PM
Best practice for database calls beatty1 C# 0 July 2nd, 2004 09:58 AM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.