best way to wait for async delegate calls to complete

  • Thread starter Thread starter ryan
  • Start date Start date
R

ryan

Hi,
I've omitted a large chunk of the code for clarity but the loop below is how
I'm calling a delegate function asynchronously. After I start the each call
I'm incrementing a counter and then making the main thread sleep until the
counter gets back to zero. The call back function for each call decrements
the counter. Is there a better way to make the thread wait until all calls
are complete besides using the counter? I've seen some things with using an
IAsyncResult but I'm not sure how to do that in my case since I'm making 1
to n calls.
Thanks,
Ryan

For iProviderIndex As Int32 = 0 To oProviders.Length - 1
oLookupReq = New Schema.LookupRequest
oLookup =
CType(Activator.CreateInstance(Type.GetType(oLookupType.class_id)),
Schema.IPrequalLookup)
Del = New LookupAsyncDelegate(AddressOf oLookup.GetPrequalResponse)
Del.BeginInvoke(oLookupReq, AddressOf LookupCallback, Del)
IncrementAsyncOps()
Next iProviderIndex

While m_async_ops > 0
Thread.Sleep(200)
End While
 
well there are always other aproaches , your solution has the big advantage
of simplicity
another aproach could be to write your todo code in a class , start these on
another thread and keep track of them with a queue class

if i think a litle more i know for sure that i can find other constructions
to get the job done , however your aproach isn`t so bad at all
and has the big advantage in my opinion that it is clear in what it does

IAsyncResult will give you a value back from the method , i do not see how
this could help in this situation

regards

Michel Posseth [MCP]
 
Ryan,

You could use the ManualResetEvent class. Create a ManualResetEvent
before the loop. Have the LookupCallback method signal the event by
calling the Set method when the m_async_ops counter gets down to 0.
After the loop wait for the event by calling the WaitOne method. I
recommend setting the m_async_ops counter's initial value to the number
of delegates your are going to invoke before the loop though.
Otherwise, you may get strange results if you're not careful.

Also, the code you've posted is not thread-safe. The while loop
checking to see if m_async_ops is > 0 may never end. The reason is
because that thread may not be seeing the changes other threads are
making. You need to have some synchronization mechanism in place to at
least do a volatile read of m_async_ops.

Brian
 
Ryan,
In addition to the other comments:

I would store the IAsyncResult values returned from BeginInvoke in a list,
that I then did a For Each over doing an EndInvoke on each one in turn.

Something like:

Public Delegate Sub DoSomething()

Public Shared Sub Something()
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(0.5))
End Sub

Public Shared Sub Main()
Dim method As DoSomething = AddressOf Something
Dim results As New ArrayList

For index As Integer = 1 To 100
Dim result As IAsyncResult = method.BeginInvoke(Nothing,
Nothing)
results.Add(result)
Next

For Each result As IAsyncResult In results
Try
method.EndInvoke(result)
Catch ex As Exception
' report the error but keep going...
Debug.WriteLine(ex, "End Invoke")
End Try
Next

End Sub

This allows the main thread to quietly wait until all the workers are
finished. Because the workers are finishing asynchronously the EndInvoke
will either return really quickly for completed workers or wait for busy
workers...

Note depending on the method called, you may want to store the Delegate
itself (the method variable) in the list also or as the AsyncState on the
BeginInvoke call...

--
Jay [MVP - Outlook]
..NET Application Architect, Enthusiast, & Evangelist
T.S. Bradley - http://www.tsbradley.net


| Hi,
| I've omitted a large chunk of the code for clarity but the loop below is
how
| I'm calling a delegate function asynchronously. After I start the each
call
| I'm incrementing a counter and then making the main thread sleep until the
| counter gets back to zero. The call back function for each call decrements
| the counter. Is there a better way to make the thread wait until all calls
| are complete besides using the counter? I've seen some things with using
an
| IAsyncResult but I'm not sure how to do that in my case since I'm making 1
| to n calls.
| Thanks,
| Ryan
|
| For iProviderIndex As Int32 = 0 To oProviders.Length - 1
| oLookupReq = New Schema.LookupRequest
| oLookup =
| CType(Activator.CreateInstance(Type.GetType(oLookupType.class_id)),
| Schema.IPrequalLookup)
| Del = New LookupAsyncDelegate(AddressOf oLookup.GetPrequalResponse)
| Del.BeginInvoke(oLookupReq, AddressOf LookupCallback, Del)
| IncrementAsyncOps()
| Next iProviderIndex
|
| While m_async_ops > 0
| Thread.Sleep(200)
| End While
|
|
 
Thanks for all of the quick responses. I'll try out these different methods
over the weekend.

Brian, as far as the m_async_ops variable goes, I am using
Interlocked.Increment and Interlocked.Decrement to change the value
m_async_ops. Won't those block another thread from reading the value or do I
need to also use some sort of synchronization while I read?

Jay, right now I'm calling the EndInvoke in the LookupCallback routine which
happens immediately when each call is finished. Is there any problems with
not calling the EndInvoke immediately? For instance, if the loop makes 5
calls and the first one happens to take 30 seconds and the other 4 take on 1
second. The first EndInvoke call will be blocking for 30 seconds even though
the other calls are finished. That reminds me of another question I have.
With the way I currently have it, does my callback function run under the
main thread or the thread that the acync call runs under?

thanks again,
Ryan
 
Ryan,

Go with Jay's idea. It's much better. In fact, that's the way I did a
similar chore in the past so I'm not really sure what I was thinking
when I suggested the alternative. My other comments are inline.
Thanks for all of the quick responses. I'll try out these different methods
over the weekend.

Brian, as far as the m_async_ops variable goes, I am using
Interlocked.Increment and Interlocked.Decrement to change the value
m_async_ops. Won't those block another thread from reading the value or do I
need to also use some sort of synchronization while I read?

Yes, you need to use some sort of synchronization during reads as well.
No, technically the Interlocked methods don't block other threads from
reading the value, but that's not the issue anyway. The issue is with
memory barriers, or more specifically, the lack thereof in this case.
A memory barrier basically tells the hardware to fetch the value from
main memory instead of using a register or on-chip cache. Likewise,
during writes it tells the hardware to go ahead and write to main
memory. The Interlocked class, like all of the synchronization
primitives, creates the memory barrier so the writes you make on other
threads will be flushed to main memory, but other threads that are
reading the counter are not guarenteed to see those changes. There are
several options:

1) Use SyncLock for both reading and writing to m_async_ops.

2) Use the Interlocked.CompareExchange method to read the value. It's
a little odd how this works so I have provided an example.

While Interlocked.CompareExchange(m_async_ops, m_async_ops,
m_async_ops) > 0
Thread.Sleep(200)
End While

3) Use Thread.VolatileRead.

4) In C# you could mark m_async_ops with the volatile keyword.

Personally, I would use #1, but the others are acceptable as well.

Read this article for more information.

Jay, right now I'm calling the EndInvoke in the LookupCallback routine which
happens immediately when each call is finished. Is there any problems with
not calling the EndInvoke immediately? For instance, if the loop makes 5
calls and the first one happens to take 30 seconds and the other 4 take on 1
second. The first EndInvoke call will be blocking for 30 seconds even though
the other calls are finished.

Yes, that's perfectly acceptable.
That reminds me of another question I have.
With the way I currently have it, does my callback function run under the
main thread or the thread that the acync call runs under?

It runs on the thread that the async call to your delegate runs on. It
isn't the thread that called Delegate.BeginInvoke.
 
Michael,

Is it not possible that the queue can lockup the process because of the
limit of the amount of parallel processed threads?

Another disadvantage can be that by the queue in fact the processes are
accessed sequential at the moment the start to become ready.

(Just a thought reading your message, not really deeply investigated).

:-)

Cor
 
Thanks for the enlightenment. I always though it was safe to read a value
without synchronization. I'll change it to use SyncLock.
 
Ryan,
| Brian, as far as the m_async_ops variable goes, I am using
| Interlocked.Increment and Interlocked.Decrement to change the value
In addition to Brian's comments: Interlocked.Decrement returns the new
decremented value, I would either use the Decrement's return value or a
SyncLock to check the variable itself.


| Jay, right now I'm calling the EndInvoke in the LookupCallback routine
which
| happens immediately when each call is finished.
I only call EndInvoke in the CallBack when no body else (such as the main
thread) cares that the async method finished or not.


| Is there any problems with
| not calling the EndInvoke immediately?
It doesn't matter when you call EndInvoke, it only matters that you call it.
If you don't call it I understand that you can leak resources.


| For instance, if the loop makes 5
| calls and the first one happens to take 30 seconds and the other 4 take on
1
| second. The first EndInvoke call will be blocking for 30 seconds even
though
| the other calls are finished.
That is the beauty of using the For Each (thinking about it, one could use a
System.Collections.Queue also). Your main process will only block for the
longest running async method, quick running async methods will return from
EndInvoke "right away".


| With the way I currently have it, does my callback function run under the
| main thread or the thread that the acync call runs under?
As Brian states the callback function runs on the thread that the ascync
call runs under.

--
Jay [MVP - Outlook]
..NET Application Architect, Enthusiast, & Evangelist
T.S. Bradley - http://www.tsbradley.net


| Thanks for all of the quick responses. I'll try out these different
methods
| over the weekend.
|
| Brian, as far as the m_async_ops variable goes, I am using
| Interlocked.Increment and Interlocked.Decrement to change the value
| m_async_ops. Won't those block another thread from reading the value or do
I
| need to also use some sort of synchronization while I read?
|
| Jay, right now I'm calling the EndInvoke in the LookupCallback routine
which
| happens immediately when each call is finished. Is there any problems with
| not calling the EndInvoke immediately? For instance, if the loop makes 5
| calls and the first one happens to take 30 seconds and the other 4 take on
1
| second. The first EndInvoke call will be blocking for 30 seconds even
though
| the other calls are finished. That reminds me of another question I have.
| With the way I currently have it, does my callback function run under the
| main thread or the thread that the acync call runs under?
|
| thanks again,
| Ryan
|
|
|
| | > Hi,
| > I've omitted a large chunk of the code for clarity but the loop below is
| > how I'm calling a delegate function asynchronously. After I start the
each
| > call I'm incrementing a counter and then making the main thread sleep
| > until the counter gets back to zero. The call back function for each
call
| > decrements the counter. Is there a better way to make the thread wait
| > until all calls are complete besides using the counter? I've seen some
| > things with using an IAsyncResult but I'm not sure how to do that in my
| > case since I'm making 1 to n calls.
| > Thanks,
| > Ryan
| >
| > For iProviderIndex As Int32 = 0 To oProviders.Length - 1
| > oLookupReq = New Schema.LookupRequest
| > oLookup =
| > CType(Activator.CreateInstance(Type.GetType(oLookupType.class_id)),
| > Schema.IPrequalLookup)
| > Del = New LookupAsyncDelegate(AddressOf oLookup.GetPrequalResponse)
| > Del.BeginInvoke(oLookupReq, AddressOf LookupCallback, Del)
| > IncrementAsyncOps()
| > Next iProviderIndex
| >
| > While m_async_ops > 0
| > Thread.Sleep(200)
| > End While
| >
|
|
 
Well i believe it is just the other way around i.o.w. with threading and a
queue you have some control over the execution ( you could even create a
construction with prio`s etc etc ) with executing asynchronous delegates
you do not have anny control at all

if it is really about speed threading and asynchronous methods are only
interesting if your proggy runs on a multi processor system , otherwise the
overhead is to costly and it would decrease performance of your project .

in the Balena book \ and VB.net remoting ( distributed programming ) book
are some nice examples that show that threading most of the times isn`t
interesting at all because of the overhead it costs .

however in the near duall processor future it is great tech.

;-)

Michel Posseth
 
Michel,

As in your sentence about Mandarine and Spanish

We completely agree and you should see my discussions in the dotnet
newsgroup where I have stated this often. I am glad that at least I found
somebody who tells the same.

Although it can be used in a lot of situations.

Cor
 
Oops,

Before you understand me wrong in past I have used often queues and this
kind of programming.

However than it was with large batch programming where I was forever
paralyzing processes and for that it is great

Cor
 
Back
Top