Threading and WaitAny

G

GCeaser

I am encountering a challenge while using WaitAny to detect when any
of my async processes have finished.

The scenario is I have a master class which instantiates two other
classes and calls a method on each asynchronously. The async method
returns output that is processed via code on the main thread.

So, I have been trying to use the WaitAny passing it an array of the
WaitHandles. I have this code in a loop that checks to see if we are
done with all async processes. The problem with this approach is that
after the first async process comes back, and I go back through the
loop to wait for the next async process to finish (using WaitAny
again) - the same async process complete re-fires the same async item
I just processed.

If have also tried to use the AutoResetEvent in the WaitAny event --
But I can figure out how to have the methods being executed
asynchronously in my class object to call the Set method on the
AutoResetEvent to trigger my main thread to continue.

I have also looked at the internet for some good examples of what I
am trying to do and have been unable to find any -- at least none if
VB code (and I am not too familiar with more complex C# syntax that
would be used in this type of an example.) If anyone has a good VB
sample that I can refer to, that would be much appreciated. It not,
the code is below. This code us currently NOT set to use the
AutoResetEvent for the reason stated above.

Any assistance would be greatly appreciated!!!

Option Strict On
Option Explicit On

Imports GATSDataSourcesDAL
Imports System.Threading

Public Class AsynTestClass
Private iobj_GATSDataSource As GATSDAL = Nothing
Private iobj_GTISDataSource As GTISDAL = Nothing

'Delegate declarations
Private Delegate Function ReturnGATSDataSet_Delegate() As DataSet
Private Delegate Function ReturnGTISDataSet_Delegate() As DataSet

'Variables to create the delegates
Private iobj_ReturnGATSDataSet_Delegate As
ReturnGATSDataSet_Delegate
Private iobj_ReturnGTISDataSet_Delegate As
ReturnGTISDataSet_Delegate

'Declare the results
Private iobj_ReturnGATSDataSet_AsyncResults As IAsyncResult
Private iobj_ReturnGTISDataSet_AsyncResults As IAsyncResult

Private li_NumberOfDataSources As Integer = 2

'Declare the WaitHandles Array
Private ia_WaitHandles(li_NumberOfDataSources - 1) As WaitHandle

'Documentation says threading items should be called from a Multi-
Threaded Appt
<MTAThread()> _

Public Function ReturnDataSet2() As Dictionary(Of String, DataSet)
'This will return an array of dataset
Dim ls_ReturnDataSet As DataSet = Nothing
Dim lb_Done As Boolean = False
Dim li_DoneCount As Integer
Dim li_IX As Integer = -1
Dim la_AsyncResults(li_NumberOfDataSources - 1) As
IAsyncResult
Dim ls_DataSourceName As String
Dim lobj_ReturnDictionary As Dictionary(Of String, DataSet) =
New Dictionary(Of String, DataSet)

'Create the Data Access Layer
iobj_GATSDataSource = New GATSDAL

'Create and call the first asyn method
iobj_ReturnGATSDataSet_Delegate = New
ReturnGATSDataSet_Delegate(AddressOf
iobj_GATSDataSource.ReturnDataSet)

'Call Async to local DLL to get the data
iobj_ReturnGATSDataSet_AsyncResults =
iobj_ReturnGATSDataSet_Delegate.BeginInvoke(Nothing, "GATS")

'Add the async results and waithandles to the array
li_IX += 1
la_AsyncResults(li_IX) = iobj_ReturnGATSDataSet_AsyncResults
ia_WaitHandles(li_IX) =
iobj_ReturnGATSDataSet_AsyncResults.AsyncWaitHandle

'Create and call the first asyn method
iobj_GTISDataSource = New GTISDAL

'Create the proxy class to call the Data Access Layer
iobj_ReturnGTISDataSet_Delegate = New
ReturnGTISDataSet_Delegate(AddressOf
iobj_GTISDataSource.ReturnDataSet)

'Call Async to local DLL to get the data
iobj_ReturnGTISDataSet_AsyncResults =
iobj_ReturnGTISDataSet_Delegate.BeginInvoke(Nothing, "GTIS")

'Add the async results and waithandles to the array
li_IX += 1
la_AsyncResults(li_IX) = iobj_ReturnGATSDataSet_AsyncResults
ia_WaitHandles(1) =
iobj_ReturnGTISDataSet_AsyncResults.AsyncWaitHandle


'Wait till all the results are back of the transaction times
out
Do Until lb_Done = True
li_IX = WaitHandle.WaitAny(ia_WaitHandles,
TimeSpan.FromSeconds(15), False)

'Get the correct asyncresults - Use the state to know the
object type
ls_DataSourceName =
la_AsyncResults(li_IX).AsyncState.ToString

Select Case ls_DataSourceName
Case "GATS"
'This will get the dataset and raise any errors
lobj_ReturnDictionary.Add(ls_DataSourceName,
iobj_ReturnGATSDataSet_Delegate.EndInvoke(la_AsyncResults(li_IX)))
Case "GTIS"
'This will get the dataset and raise any errors
lobj_ReturnDictionary.Add(ls_DataSourceName,
iobj_ReturnGTISDataSet_Delegate.EndInvoke(la_AsyncResults(li_IX)))
End Select

'Decide if we should end the loop
li_DoneCount += 1
If li_DoneCount > ia_WaitHandles.GetUpperBound(0) Then
lb_Done = True
End If

Loop

Return lobj_ReturnDictionary

End Function

End Class
 
P

Peter Duniho

I am encountering a challenge while using WaitAny to detect when any
of my async processes have finished.

The scenario is I have a master class which instantiates two other
classes and calls a method on each asynchronously. The async method
returns output that is processed via code on the main thread.

So, I have been trying to use the WaitAny passing it an array of the
WaitHandles. I have this code in a loop that checks to see if we are
done with all async processes. The problem with this approach is that
after the first async process comes back, and I go back through the
loop to wait for the next async process to finish (using WaitAny
again) - the same async process complete re-fires the same async item
I just processed.

Things I don't understand about your question:

* Why are your arrays, ia_WaitHandles and la_AsyncResults, being
initialized to hold one fewer elements than you intend to put in them? In
this case, those arrays appear to be initialized to only a single element
long.

* Given the above, why does this line not generate an exception at
run-time, seeing how it attempts to set the second element in an array
that's only 1 element long:

ia_WaitHandles(1) =
iobj_ReturnGTISDataSet_AsyncResults.AsyncWaitHandle

* Regardless, why do you hard-code the array index for the handle in
the above line of code, rather than using li_IX as you do elsewhere?

And finally, this statement in your post:
If have also tried to use the AutoResetEvent in the WaitAny event --
But I can figure out how to have the methods being executed
asynchronously in my class object to call the Set method on the
AutoResetEvent to trigger my main thread to continue.

Even if I replace "I can figure" with "I can't figure" to try to make it
make more sense, I don't understand the statement. All you need to do is
pass an event handle you create to the delegate doing the work somehow.
Passing parameters to delegates isn't hard, so if you're having trouble
doing this, you should be more specific about what problem it is you have.

All that said, it seems to me that the most direct way to fix the code
you've got would be to just not wait on handles that have already
completed. You should be doing this anyway, because once you call
EndInvoke, the wait handle you've put into the array is not guaranteed to
be valid. But beyond that, not waiting on a wait handle that's always
been completed is the obvious solution to not having to deal with the wait
handle remaining signaled.

An alternative solution would be to simply reset the event handle manually
yourself after it's signaled. But as I mentioned above, after calling
EndInvoke you are not guaranteed to have a valid event handle, and so
that's not a solution you should choose. As an alternative to the
alternative, you could just create a dummy WaitHandle (manual or
auto-reset, it doesn't matter) that never gets set and replace the one you
got from the IAsyncResult object with the dummy one, after that
IAsyncResult gets signaled. IMHO, that solution would be a hack and
inappropriate in any well-written code, but it would work as well.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top