Home All Groups Group Topic Archive Search About

AsyncResult, Calback and WaitHandles

Author
2 Mar 2007 5:04 PM
deja
I have a web service that reads data from the database in a number of
asynchronous calls.

I have a callback for each call so that I can call EndInvoke so that I
can trap any errors.

I also have a WaitHandle.WaitAll.

The trouble is that the WaitAll completes before the callbacks are all
complete. So the web service returns results despite the fact that one
of the database calls errored.

How do I trap the error and bubble it up to the original web service
thread?

Thanks
Phil

Author
2 Mar 2007 8:49 PM
Bob Altman
If I understand this correctly, you use the WaitAll to block the main thread
until the asynchronous calls are done.  But it unblocks before the async
callbacks have a chance to execute.

Under the covers, each async process signals its associated Event object
prior to calling its callback routine.  Thus the fatal flaw in your existing
design:  The Event objects are (by design) signalled before the callbacks
are delivered.

I would use a SyncLock (lock in C#) block to arbitrate access to a private
variable that keeps track of the progress of asynchronous work.  When the
final async callback completes, you can use whatever private mechanism you
like best to wake up or queue a call onto the main thread.

The main point here is that you need to implement your own private
synchronization mechanism.  When dealing with async code, don't try to use
both the WaitHandle and the Callback.  It's an either/or proposition.

  - Bob

<d***@2bytes.co.uk> wrote in message
Show quote
news:1172855054.523482.137240@v33g2000cwv.googlegroups.com...
>I have a web service that reads data from the database in a number of
> asynchronous calls.
>
> I have a callback for each call so that I can call EndInvoke so that I
> can trap any errors.
>
> I also have a WaitHandle.WaitAll.
>
> The trouble is that the WaitAll completes before the callbacks are all
> complete. So the web service returns results despite the fact that one
> of the database calls errored.
>
> How do I trap the error and bubble it up to the original web service
> thread?
>
> Thanks
> Phil
>
Author
3 Mar 2007 11:15 AM
deja
thanks for this Bob. Am starting to understand now. So if the main
thread needs results from the async process then a callback is not
going to be any good to me if that main thread is a web service method
because by the time the callback comes the main thread might have
gone?

So I will use Wait and then do the EndInvoke after that. I need to
figure out how WaitAny works because I've tried that but am not quite
sure how you determine which waithandle to process.... I could use
WaitAll but WaitAny seems more efficient.

Also, just to check regarding the callback, if I was using, for
example a Windows app, and wanted to, as you put it, wake up the main
thread or queue a call, how would I go about doing that> Do you have
any sample code that shows how the worker thread communicates back to
the main thread?

thanks a lot for this.

Show quote
On Mar 2, 8:49 pm, "Bob Altman" <r...@nospam.nospam> wrote:
> If I understand this correctly, you use the WaitAll to block the main thread
> until the asynchronous calls are done.  But it unblocks before the async
> callbacks have a chance to execute.
>
> Under the covers, each async process signals its associated Event object
> prior to calling its callback routine.  Thus the fatal flaw in your existing
> design:  The Event objects are (by design) signalled before the callbacks
> are delivered.
>
> I would use a SyncLock (lock in C#) block to arbitrate access to a private
> variable that keeps track of the progress of asynchronous work.  When the
> final async callback completes, you can use whatever private mechanism you
> like best to wake up or queue a call onto the main thread.
>
> The main point here is that you need to implement your own private
> synchronization mechanism.  When dealing with async code, don't try to use
> both the WaitHandle and the Callback.  It's an either/or proposition.
>
>   - Bob
>
> <d***@2bytes.co.uk> wrote in message
>
> news:1172855054.523482.137240@v33g2000cwv.googlegroups.com...
>
> >I have a web service that reads data from the database in a number of
> > asynchronous calls.
>
> > I have a callback for each call so that I can call EndInvoke so that I
> > can trap any errors.
>
> > I also have a WaitHandle.WaitAll.
>
> > The trouble is that the WaitAll completes before the callbacks are all
> > complete. So the web service returns results despite the fact that one
> > of the database calls errored.
>
> > How do I trap the error and bubble it up to the original web service
> > thread?
>
> > Thanks
> > Phil
Author
4 Mar 2007 1:19 AM
Bob Altman
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

<d***@2bytes.co.uk> wrote in message
Show quote
news:1172920525.690835.135760@s48g2000cws.googlegroups.com...
> thanks for this Bob. Am starting to understand now. So if the main
> thread needs results from the async process then a callback is not
> going to be any good to me if that main thread is a web service method
> because by the time the callback comes the main thread might have
> gone?
>
> So I will use Wait and then do the EndInvoke after that. I need to
> figure out how WaitAny works because I've tried that but am not quite
> sure how you determine which waithandle to process.... I could use
> WaitAll but WaitAny seems more efficient.
>
> Also, just to check regarding the callback, if I was using, for
> example a Windows app, and wanted to, as you put it, wake up the main
> thread or queue a call, how would I go about doing that> Do you have
> any sample code that shows how the worker thread communicates back to
> the main thread?
>
> thanks a lot for this.
>
> On Mar 2, 8:49 pm, "Bob Altman" <r...@nospam.nospam> wrote:
>> If I understand this correctly, you use the WaitAll to block the main
>> thread
>> until the asynchronous calls are done.  But it unblocks before the async
>> callbacks have a chance to execute.
>>
>> Under the covers, each async process signals its associated Event object
>> prior to calling its callback routine.  Thus the fatal flaw in your
>> existing
>> design:  The Event objects are (by design) signalled before the callbacks
>> are delivered.
>>
>> I would use a SyncLock (lock in C#) block to arbitrate access to a
>> private
>> variable that keeps track of the progress of asynchronous work.  When the
>> final async callback completes, you can use whatever private mechanism
>> you
>> like best to wake up or queue a call onto the main thread.
>>
>> The main point here is that you need to implement your own private
>> synchronization mechanism.  When dealing with async code, don't try to
>> use
>> both the WaitHandle and the Callback.  It's an either/or proposition.
Author
7 Mar 2007 10:04 AM
deja
Show quote
On Mar 4, 1:19 am, "Bob Altman" <r...@nospam.com> wrote:
> 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
>
> <d***@2bytes.co.uk> wrote in message
>
> news:1172920525.690835.135760@s48g2000cws.googlegroups.com...
>
> > thanks for this Bob. Am starting to understand now. So if the main
> > thread needs results from the async process then a callback is not
> > going to be any good to me if that main thread is a web service method
> > because by the time the callback comes the main thread might have
> > gone?
>
> > So I will use Wait and then do the EndInvoke after that. I need to
> > figure out how WaitAny works because I've tried that but am not quite
> > sure how you determine which waithandle to process.... I could use
> > WaitAll but WaitAny seems more efficient.
>
> > Also, just to check regarding the callback, if I was using, for
> > example a Windows app, and wanted to, as you put it, wake up the main
> > thread or queue a call, how would I go about doing that> Do you have
> > any sample code that shows how the worker thread communicates back to
> > the main thread?
>
> > thanks a lot for this.
>
> > On Mar 2, 8:49 pm, "Bob Altman" <r...@nospam.nospam> wrote:
> >> If I understand this correctly, you use the WaitAll to block the main
> >> thread
> >> until the asynchronous calls are done.  But it unblocks before the async
> >> callbacks have a chance to execute.
>
> >> Under the covers, each async process signals its associated Event object
> >> prior to calling its callback routine.  Thus the fatal flaw in your
> >> existing
> >> design:  The Event objects are (by design) signalled before the callbacks
> >> are delivered.
>
> >> I would use a SyncLock (lock in C#) block to arbitrate access to a
> >> private
> >> variable that keeps track of the progress of asynchronous work.  When the
> >> final async callback completes, you can use whatever private mechanism
> >> you
> >> like best to wake up or queue a call onto the main thread.
>
> >> The main point here is that you need to implement your own private
> >> synchronization mechanism.  When dealing with async code, don't try to
> >> use
> >> both the WaitHandle and the Callback.  It's an either/or proposition.


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......
Author
8 Mar 2007 9:57 PM
Bob Altman
Show quote
> 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......

That's curious.  It's behaving as if the first WaitHandle is still signalled
the second time around.  I would have expected that the delegate object
would return a reference to an AutoResetEvent object, which would reset its
"signalled" state when your WaitAny() call completes.  (BTW, you left off
the timeout parameter in the WaitAny call, so you will never see a
"TimedOut" result.)

Take a look at the Type of the object returned by res1.AsyncWaitHandle.

Your other option is to simply call the two EndInvoke routines in lieu of
the entire "for" loop:

  res1 = accDel.BeginInvoke;
  res2 = ordDel.VeginInvoke;

  accDel.EndInvoke(res1);
  ordDel.EndInvoke(res2);

This code blocks until both async routines complete, which is the same
behavior that your example code exhibits.
Author
23 Mar 2007 9:54 PM
deja
Show quote
On 8 Mar, 21:57, "Bob Altman" <r...@nospam.nospam> wrote:
> > thanks for this. There's something I obviously haven't quite got
> > sussed. To do withWaitAny. If I use SqlCommand.BeginExecuteReader
> > then I can useWaitAnyand use the index returned to process the
> > relevant results.
>
> > If I use a delegate to one of my own functions, use BeginInvoke and
> > then useWaitAny, I keep getting 0 as the WaitHandle index which then
> > causes an error because you can only call EndInvokeonce.
>
> > 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......
>
> That's curious.  It's behaving as if the first WaitHandle is still signalled
> the second time around.  I would have expected that the delegate object
> would return a reference to an AutoResetEvent object, which would reset its
> "signalled" state when yourWaitAny() call completes.  (BTW, you left off
> the timeout parameter in theWaitAnycall, so you will never see a
> "TimedOut" result.)
>
> Take a look at the Type of the object returned by res1.AsyncWaitHandle.
>
> Your other option is to simply call the two EndInvoke routines in lieu of
> the entire "for" loop:
>
>   res1 = accDel.BeginInvoke;
>   res2 = ordDel.VeginInvoke;
>
>   accDel.EndInvoke(res1);
>   ordDel.EndInvoke(res2);
>
> This code blocks until both async routines complete, which is the same
> behavior that your example code exhibits.

Aha, I didn't see your reply until now but today I figured out the
problem anyway. It returns a ManualResetEvent object - don't know why,
don't know what determines it - but anyway, when I cast the WaitHandle
to a ManualResetEvent and use the Reset method after getting the
WaitAny , it all works fine. I write this because after searching for
the solution in newsgroups I never found the answer - so maybe this
will help someone in the future.

thanks

AddThis Social Bookmark Button