Threading Issue

B

Brad Brening

I am building a class that has a function that I would like to run
asycronously. When the function is complete I would like to notify the
calling thread by raising an event or using a delegate. The handler for the
event (or the delegate callback) should run in the parent thread - not on
the asyncronous thread created while the function ran.

So, basically, I want my parent (form, class, control) to call my class
function - which spawns a thread and does some work. Once this work is done
I want to notify the parent and allow it to process the result in the parent
thread. Simple? Maybe. I can't make it happen.

I tried messing around with the AsyncCallback and IAsyncResult methods using
delegates. This is all fine and good, however the callback runs in the
spawned thread and not the parent thread.

I found some dirty hacks that referenced the parent form when the class is
instantiated and using the forms "BeginInvoke" method to perform the
operation. That would be good if I could count on my class always being
instantiated by a form - but this class could be called from another class
or even a user control. That rules out that avenue.

So, my question is this. What is the best way to achieve asyncronous
execution with the ability to notify the main thread when the operation is
complete? Further, this ability needs to be totally invisible to the client.
Am I missing something with using a Delegate function and the ".BeginInvoke"
and ".EndInvoke" methods that is keeping me from being notified on the main
thread?

Here is my code for the class:


visual basic code:
Imports System
Imports System.Runtime.Remoting.Messaging

Public Delegate Function QueryCallback() As Boolean

Public Class cQuery

Public Test As Integer

Public Function Query() As Boolean
Dim I As Integer
' just tie up this thread for a little while.
For I = 1 To 1000000
Test = I
Next
Query = True
End Function

Public Function BeginQuery(ByVal aCallBack As AsyncCallback, ByVal
AsyncState As Object) As IAsyncResult
Dim cbQuery = New QueryCallback(AddressOf Query)
BeginQuery = cbQuery.BeginInvoke(aCallBack, AsyncState)
End Function

Public Function EndQuery(ByVal iaResult As IAsyncResult) As Boolean
Dim cbQuery As QueryCallback
Dim result As AsyncResult
result = CType(iaResult, AsyncResult)
cbQuery = result.AsyncDelegate
EndQuery = cbQuery.EndInvoke(iaResult)
End Function
End Class



Here is the code for my Form (to test):


visual basic code:
' snip - basic form with a button
Private tc As New cQuery()

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim ac As New AsyncCallback(AddressOf MyCallback)
If Thread.CurrentThread.Name Is Nothing Then
Thread.CurrentThread.Name = "MAIN THREAD"
Dim Result As IAsyncResult = tc.BeginQuery(ac, tc)
MessageBox.Show("Current Thread Name = " &
Thread.CurrentThread.Name)
Console.WriteLine("LEAVING BUTTON1_CLICK HANDLER!")
End Sub

Private Sub MyCallback(ByVal result As IAsyncResult)
Console.WriteLine("ENTERING CALLBACK")
tc.EndQuery(result)
MessageBox.Show("Current Thread Name = " &
Thread.CurrentThread.Name)
End Sub
 
G

Guest

I understand your problem. More generally, one might have multiple threads
and each thread might have multiple outputs. Raising an event on a
particular thread sounds like a nice idea, but I dont think it can be done.
My answer to this program organization issue is as follows:

1. A class that contains a Queue property.
2. Thread-safe Enqueue and Dequeue methods (worker threads enqueue, the
calling thread dequeues)
3. What gets enqueued and dequeued is always an array of objects (I
sacrifice strong typing for flexibility). In my game, the zero'th entry is
always a string that defines what follows. Enqueue has an arg like 'ByVal
ParamArray Data() As Object'.
Dequeue is a function returning Object().
4. The class also has Form and Timer properties. If set to other than
Nothing, whenever data is enqueued, the class does an Invoke to enable the
timer for an immediate tick, and this is how the worker thread dings the main
thread. If you prefer, your main thread could just poll the queue.
5. Not essential to the concept is the fact that I actually use three
queues, flash, normal, and idle. Worker threads can enqueue to any queue,
but dequeue always gives preference to flash over normal, and normal over
idle. It is a useful gimmick for my apps, but it is not essential.
6. Total size of the class is about 60 lines of VB, maybe 100 total with
comments and whitespace.

I know this sounds like overkill given your question, but my multi-thread
programs evolved in this direction, and I am content with it. To me it is
just a bit of threading infrastructure. I also don't claim it is the best
way - it is just my way.

Finally, be advised - if your main thread is a form thread, then the 'dirty
hack' of some variant of Invoke is absolutely necessary.
 

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