I have only a passing familiarity with Web Services, but I don't see why
that should stop me from offering advice. ;-)
If your web service wants to kick off some asynchronous processing, then it
seems to me that you have several options:
1. You can block the web service thread until the async operations have
completed. You would want to do this if, for example, it takes less time to
run your async operations in parallel than it would to serialize them (call
them them one after another synchronously). In this case, your
initialization code would be:
private m_myEvent As New Threading.AutoResetEvent(False)
Your web service code would be:
StartAsyncStuff()
m_myEvent.WaitOne()
ProcessAsyncOperationResults
Your async callback routine would be:
Try
EndXxx()
Catch WhateverException
Store exception info in a global variable
End Try
' Wake up the main thread
If (all async work is done) Then m_myEvent..Set
2. You can start the async processing and immediately return control back to
the web method caller. In this case, the caller would be clueless about the
results of the async processing. You could use a local logging mechanism
(such as the event log) to record any problems that arise from your async
processing.
3. You can combine options 1 and 2 by having your routine (let's call it
BeginXxx) allocate a state object in a hash table or dictionary with a
unique index. BeginXxx would start the async processing, passing the state
object to each async routine's "state" argument, then return the unique
index to the web method caller. As the async routines complete, BeginXxx
would fetch the state object from the IAsyncResult.State member, and store
the results in the state object. You would then provide a companion routine
(let's call it EndXxx) which takes as an argument the index returned by
BeginXxx. EndXxx fetches the state object from the dictionary and blocks
(as shown above) until all of the async processing is done.
On your last question (how to queue a call to the main thread): If your app
is a Windows Forms app, then all of the event handlers that are called in
your "code behind the form" are called on the "main UI" thread. Under the
covers, this is actually the thread that spends most of its time waiting for
the Windows message pump to deliver Windows messages to it. If, for
example, you call someStream.BeginWrite() in a button's Click event then the
completion callback will happen on a thread pool thread, not on the main UI
thread. If you try to access the main form or any of its controls in that
callback then you will either get unpredictable results (it may work or you
may get some really strange error) in VS 2003, or you will get an exception
in VS 2005. The exception to the "don't access the main form or its
controls" rule is InvokeRequired, Invoke, and BeginInvoke. I use this
design pattern to transfer control from a routine called on a non-UI thread
back to the UI thread:
Sub SomeRoutine(SomeArgs)
If Me.InvokeRequired() Then
' Queue the call to the UI thread
Dim deleg As New SomeRoutineDeleg(AddressOf SomeRoutine)
Dim args() as Object = {SomeArgs}
Me.BeginInvoke(deleg, args)
Else
' We're on the UI thread...
do the work
End If
End Sub
Good luck...
- Bob
thanks for this. There's something I obviously haven't quite got
sussed. To do with WaitAny. If I use SqlCommand.BeginExecuteReader
then I can use WaitAny and use the index returned to process the
relevant results.
If I use a delegate to one of my own functions, use BeginInvoke and
then use WaitAny, I keep getting 0 as the WaitHandle index which then
causes an error because you can only call EndInvoke once.
Do I have to use this AutoResetEvent within the function called? How
do I do this? - my function is currently called like this:
WaitHandle[] wh = new WaitHandle[2];
accDelegate accDel = new accDelegate(new Account().GetDetails);
IAsyncResult res1 = accDel.BeginInvoke(123);
ordDelegate ordDel = new ordDelegate(new Order().GetDetails);
IAsyncResult res2 = ordDel.BeginInvoke(123);
wh[0] = res1.AsyncWaitHandle;
wh[1] = res2.AsyncWaitHandle;
Hashtable accounts;
Hashtable orders;
for (int i=0;i<2;i++)
{
int index = WaitHandle.WaitAny(wh);
if (index == WaitHandle.WaitTimeout)
throw new Exception("Timeout.");
switch(index)
{
case 0:
accounts = accDel.EndInvoke(res1);
break;
case 1:
orders = ordDel.EndInvoke(res2);
break;
}
}
carry on........
But I get index returned as 0 twice in a row......