Threading Issue

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Brad Brening

    #1

    Threading Issue

    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 "BeginInvok e" 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 ".BeginInvo ke"
    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.Messag ing

    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(ByVa l aCallBack As AsyncCallback, ByVal
    AsyncState As Object) As IAsyncResult
    Dim cbQuery = New QueryCallback(A ddressOf Query)
    BeginQuery = cbQuery.BeginIn voke(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.AsyncDel egate
    EndQuery = cbQuery.EndInvo ke(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(B yVal sender As System.Object, ByVal e As
    System.EventArg s) Handles Button1.Click
    Dim ac As New AsyncCallback(A ddressOf MyCallback)
    If Thread.CurrentT hread.Name Is Nothing Then
    Thread.CurrentT hread.Name = "MAIN THREAD"
    Dim Result As IAsyncResult = tc.BeginQuery(a c, tc)
    MessageBox.Show ("Current Thread Name = " &
    Thread.CurrentT hread.Name)
    Console.WriteLi ne("LEAVING BUTTON1_CLICK HANDLER!")
    End Sub

    Private Sub MyCallback(ByVa l result As IAsyncResult)
    Console.WriteLi ne("ENTERING CALLBACK")
    tc.EndQuery(res ult)
    MessageBox.Show ("Current Thread Name = " &
    Thread.CurrentT hread.Name)
    End Sub


  • AMercer

    #2
    RE: Threading Issue

    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.

    Comment

    Working...