Question

I'm writing an ASP.NET web service using C# that has a DoLookup() function. For each call to the DoLookup() function I need my code to execute two separate queries: one to another web service at a remote site and one to a local database. Both queries have to complete before I can compile the results and return them as the response to the DoLookup method. The problem I'm dealing with is that I want to make this as efficient as possible, both in terms of response time and resource usage on the web server. We are expecting up to several thousand queries per hour. Here's a rough C#-like overview of what I have so far:

public class SomeService : System.Web.Services.WebService
{
    public SomeResponse DoLookup()
    {
        // Do the lookup at the remote web service and get the response 
        WebResponse wr = RemoteProvider.DoRemoteLookup();   

        // Do the lookup at the local database and get the response
        DBResponse dbr = DoDatabaseLookup();

        SomeResponse resp = new SomeResponse( wr, dbr);

        return resp;
    }
}

The above code does everything sequentially and works great but now I want to make it more scalable. I know that I can call the DoRemoteLookup() function asynchronously ( RemoteProvider has BeginRemoteLookup / EndRemoteLookup methods) and that I can also do the database lookup asynchronously using the BeginExecuteNonQuery / EndExecuteNonQuery methods.

My question (finally) is this: how do I fire both the remote web service lookup AND the database lookup simultaneously on separate threads and ensure that they have both completed before returning the response?

The reason I want to execute both requests on separate threads is that they both potentially have long response times (1 or 2 seconds) and I'd like to free up the resources of the web server to handle other requests while it is waiting for responses. One additional note - I do have the remote web service lookup running asynchronously currently, I just didn't want to make the sample above too confusing. What I'm struggling with is getting both the remote service lookup AND the database lookup started at the same time and figuring out when they have BOTH completed.

Thanks for any suggestions.

Was it helpful?

Solution

You can use a pair of AutoResetEvents, one for each thread. At the end of thread execution, you call AutoResetEvents.Set() to trigger the event.

After spawning the threads, you use WaitAll() with the two AutoResetEvents. This will cause the thread to block until both events are set.

The caveat to this approach is that you must ensure the Set() is guarantee to be called, otherwise you will block forever. Additionally ensure that with threads you exercise proper exception handling, or you will inadvertently cause more performance issues when unhanded exceptions cause your web application to restart.

MSDN Has sample code regarding AutoResetEvent usage.

OTHER TIPS

See Asynchronous XML Web Service Methods, How to: Create Asynchronous Web Service Methods and How to: Chain Asynchronous Calls with a Web Service Method.

But note the first paragraph of those articles:

This topic is specific to a legacy technology. XML Web services and XML Web service clients should now be created using Windows Communication Foundation (WCF).


BTW, doing things the way these articles say is important because it frees up the ASP.NET worker thread while the long-running task runs. Otherwise, you might be blocking the worker thread, preventing it from servicing further requests, and impacting scalability.

Assuming you can have a callback function for both the web request and the database lookup then something along these lines may work

bool webLookupDone = false;
bool databaseLookupDone = false;

private void FinishedDBLookupCallBack()
{
    databaseLookupDone = true;
    if(webLookupDone)
    {
        FinishMethod();
    }
}

private void FinishedWebLookupCallBack()
{
    webLookupDone = true;
    if(databaseLookupDone)
    {
        FinishMethod();
    }
}

I guess I don't have enough rep to upvote nor to comment. So this is a comment on John Saunders answer and Alan's comment on it.

You definitely want to go with John's answer if you are concerned about scalability and resource consumption.

There are two considerations here: Speeding up an individual request, and making your system handle many concurrent requests efficiently. The former both Alan's and John's answer achieve by performing the external calls in parallel.

The latter, and it sounds like that was your main concern, is achieved by not having threads blocked anywhere, i.e. John's answer.

Don't spawn your own threads. Threads are expensive, and there are already plenty of threads in the IO Threadpool that will handle your external calls for you if you use the asynch methods provided by the .net framework.

Your service's webmethod needs to be asynch as well. Otherwise a worker thread will be blocked until your external calls are done (it's still 1-2 seconds even if they run in parallel). And you only have 12 threads per CPU handling incoming requests (if your machine.config is set according to recommendation.) I.e. you would at most be able to handle 12 concurrent requests (times the # of CPUs). On the other hand if your web method is asynch the Begin will return pretty much instantenously and the thread returned to the worker thread pool ready to handle another incoming request, while your external calls are being waited on by the IO completion port, where they will be handled by threads from the IO thread pool, once they return.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top