Pete,
This is the way I have it setup. I only included enough code so you
understand the logic. I stripped out a bunch that is not required. So
the part where I need to optimze is the GetSearchResultsArray(). I
would like to fire the three GetResults at the same time and then be
able to merge the data together into one table (no particular order).
Okay, the "no particular order" is helpful. If the order did matter, that
could be easily addressed, but it does make the code simpler to not have
to worry about it.
Let's start with the suggestions and code that Nicholas posted, since his
basic response is very useful.
Based on that response, I'd offer a couple of observations:
* First, the difference between his two suggestions -- calling
EndGetResponse() in sequence for each request, versus setting a waitable
event -- is not very great, at least not as he demonstrated it. In either
case, the code will simply stop before exiting the method that starts all
three requests, so they have the same effect.
Where setting the event handle might be useful is if you had some code
_somewhere else_ that would wait on it, in a different thread. For
example, let's say you ran the code he posted in the main thread in
response to something, but had a different thread sitting around waiting
to process completed data retrievals. Then that different thread could
use the waitable event as its signal to do more work. Of course, in that
scenario you wouldn't create the waitable event in the code that starts
the requests. It'd be stored somewhere more accessible so that the other
thread could already be waiting on it.
* Second, his sample provides a very good illustration of the
synchronization required for the DataTable. I like to follow Jon's advice
to not lock using the actual object, but rather to create a separate
"object" instance for use in locking. But otherwise, his sample shows
what I meant when I wrote of the need to address concurrency issues by
synchronizing access to the DataTable.
* Finally, I think Nicholas meant to just write "callback" instead of
"callback1", "callback2", and "callback3" when he calls BeginGetResponse().
Now, how would I adjust his sample to suit the description you've given
above?
I would get rid of the synchronization at the end of his method, as well
as the waitable event altogether. I would also, of course, create a new
object for locking the DataTable. Finally, without the waitable event,
instead I would just call whatever code you have that needs to be called
when all of the requests have completed.
So, taking Nicholas's code as the starting point, here's what it'd look
like instead:
public void MyMethod()
{
// Create the three web requests.
HttpWebRequest wr1 = ...;
HttpWebRequest wr2 = ...;
HttpWebRequest wr3 = ...;
// This is the number of web requests that still have to complete.
int requestsToComplete = 3;
// The data table to return.
DataTable dt = ...;
// [an object used to synchronize access to the DataTable -- Pete]
object objLock = new object();
// The event which will be called to indicate that processing is done.
// The async callback which will process the data. You will need
// separate code for each if they have different routines to
// populate the data table.
AsyncCallback callback =
delegate(IAsyncResult ar)
{
// Get the request from the state.
// [note that I've changed to a straight case from the "as"
// that Nicholas had. I only use "as" if I've got some code
// that will actually deal with a failed cast. Otherwise,
// you just get a delayed exception, and a less-useful oneat
// that, since the exception is a null reference instead of
the
// more informative invalid cast that actually describes what
// went wrong -- Pete]
HttpWebRequest request = (HttpWebRequest)ar.AsyncState;
// Call EndGetResponse.
using (HttWebResponse response = (HttpWebResponse)
request.EndGetResponse(ar))
{
// Add to the data table here. This is the code specific
to the request.
// You have to synchronize access to the table as well.
lock (objLock)
{
// Process the response here and add the rows you need
to.
// [here is where you'd convert the response to
DataTable and
// then call DataTable.Merge() with the results, for
example.
// Noting, of course, that in this scenario it might
be easier
// to just add the data as it's generated from the
response to
// the original table. But if that were really true,
maybe you
// would have done it that way in the original code
too, so I don't
// really know.
-- Pete]
}
}
// Decrement the count on the requests to complete. If it is
// zero, then fire the event.
if (Interlocked.Decrement(ref requestsToComplete) == 0)
{
// [here you'd call whatever method needs executing when
all of the
// data has been retrieved. If that method includes any
calls to update
// things in the UI, you'll either need to use
Control.Invoke() here to
// call that method, or in that method use
Control.Invoke() to do the
// UI-specific stuff -- Pete]
}
};
// Begin the calls here.
wr1.BeginGetResponse(callback, wr1);
wr2.BeginGetResponse(callback, wr2);
wr3.BeginGetResponse(callback, wr3);
}
Hope that helps.
Pete