none
Events, Threading, Cross Thread Invoking... RRS feed

  • Question

  • Well,

    I'm working on a threaded class that basically runs a process in an internal thread, and raises events at specific triggers.  These events, however, will most likely need to be synchronized with the main thread.  I've written similar multi-threaded facets before, however at the time I was handling the threading in a Form which comes with some nifty invoking methods. 

    My initial Form Design was:
    Create Background Worker Thread
    ON WorkThread Execute -> Load my Processing Class and proceed to process the data. 
    On WorkThread Progress Event Call backs, evaluate the stage of the Process
    For some such Stages in the Process - > Me.EndInvoke(Me.BeginInvoke(SpecialMethod(obj)))
    Continue to process thread until completion.


    This worked great for what I was handling, because there were some control manipulations that needed to occur on the fly during the processing, but it was prettier (and smoother) to handle these things in multiple threads.

    Now I'm working on a self contained class that is threaded and running a process (handling Named Pipe communications) and as the pipe is programmed to read in a message or some other data, I need to raise an event so the data can be processed.  At that Time, I know can simply "RaiseEvent" but if that event (and the subsequent data passed in the event arguments parameter) is being excuted on the Pipe thread, it can't access the main thread without causing a crossthread exception.  So I want the RaiseEvent to occur in the owning thread (the thread that spawned the pipethread object) and need a few pointers on how that can be achieved.

    I did notice in Reflector that many of the Event Delegate contain BeginInvoke/EndInvoke/Invoke methods, and was curious if events are naturally ThreadSafe or if they can be attributed to force them to synchronize with the main thread or something. 

    Thanks
    Jaeden "Sifo Dyas" al'Raec Ruiner
    "Never Trust a computer. Your brain is smarter than any micro-chip."
    Wednesday, April 15, 2009 4:09 PM

Answers

All replies

  • Check this thread for a generic solution.
    Hans Passant.
    • Proposed as answer by Stephen ClearyMVP Sunday, April 19, 2009 6:45 PM
    • Marked as answer by Zhi-Xin Ye Wednesday, April 22, 2009 10:37 AM
    Wednesday, April 15, 2009 4:36 PM
    Moderator
  • I feel obligated to point out that Event-Based Asynchronous Programming (MSDN) results in more reusable classes (e.g., WPF). The ISynchronizeInvoke/SynchronizationObject approach will only work with Windows Forms, unless you use an object that can translate the ISynchronizeInvoke calls to SynchronizationContext calls.

            -Steve
    Wednesday, April 15, 2009 6:39 PM
  • Where would a good How to/usage documentation guide exist for the implementation of the SynchronizationContext class. 

    I've looked into it, and that is pretty much exactly what I need, but I need it to exist two way. 

    Primary Class is a wrapper for Named Pipe communication.  I've got my basic NamedPipe which handles all the communication, as an abstract it requires to be inherited, which it is, into two other Pipe classes, Server and Client.  These basically are wrappers for the IO.Pipes classes but it handles all the funky messaging in a more manageable fashion.  Thus I can create wait timeouts for connections, etc.  I can just say: SendMessage(blah") and it is sent, as well as creating other commands like "Wait" which causes the receiving pipe (whether client or server) to enter a wait period until a "Ready" command is transmitted.  Basically, this handles how I like to communicate across pipe lines easily and fluidly. 

    But, for more complex pipe commuication operations, I would like to have an event driven version of these classes.  The Class is run, and in the background (thread) the pipe is opened and connected, and then enters a loop. 

    do
    If Me.Connected then
       if Me._pipe.CheckCommand() then
          select case _pipe.PipeCommand
             case PipeCommandsEnum.CMD_MSG : DoMessageReceived()
             Case PipeCOmmandsEnum.CMD_WAIT: DoSuspend()
          end select
       end if
    end if
    Application.DoEvents()
    loop until (_pipe.PipeCommand = CMD_QUIT) orelse Terminated

    As you can see the general idea is to wait for any signal from the pipe and raise the appropriate event when activity occurs on the pipe between processes.  But for the kicker:
    If i write this in a single threaded environment the problem exists when one side of the pipe is just processing stuff. 
    Basically, while processing a command from the pipe, I exist in an event method that does not return to the pipe loop until it completes.  The idea was to make the Pipe Event class handle the internal listening to the pipe, but not be the sole execution of the computation the pipe is supposed to facilitate.  Right now, i'm having to write very convoluted Select statements that check some stage variable to indicate what is currently going on on both sides, and then send a READY command for the resumed event to process what to do next.  because other wise both pipes just sit there listening. 

    By multi threading the inner workings of this event class.  The pipe can be started FNF style, and execution returns to my main thread.  At which point I can create whatever processing loop I need to send commands across the pipe, and wait for events.  Maybe i'm thinking about it more twisted than I should but it seems that without threads I have to wait for a signal in order to send a signal. 

    Class - Thread 1 (main thread or other doesn't matter)
      -> creates internal Thread 2 which runs the internal pipe communciation
      -> events are fired on Thread 1 the creating thread of this object
      -> Messages are queued into the Thread 2 processing so as it loops it checks if a message is to be delivered and delivers it

    A SynchronizationContext would resolve this issue, allowing me to not only Fire off events in the creating thread, but allow the creating thread to queue up messages into the pipe which exists in an internal thread space. I used to do this kind of programming very easily in Delphi, but switching over to .Net I have to relearn some techniques.  The default SyncContext is very barebones which is why i need to find a guide that helps explain how to handle some of the marshalling between different thread spaces to create the post/send async and sync'd calls to each other.

    Thanks
    Jaeden "Sifo Dyas" al'Raec Ruiner

    "Never Trust a computer. Your brain is smarter than any micro-chip."
    Wednesday, April 22, 2009 2:42 PM
  • Hmya, you traded one problem for another.  While you original approach was certainly very ugly, your new one solves some of the problems but created several new ones.  Hard ones, synchronizing threads is difficult.  I suspect the real issue here is your use of a named pipe to try to shuttle state between otherwise disconnected chunks of code.  Pipes are not very good at that, everything needs to be encoded.  Forcing you to maintain state awareness on either side of the pipe.  That's brittle.  Minor code changes tend to avalanche since a new state representation is required.  Errors are fatal when the stored state no longer agrees with actual state.

    Look for a richer way to implement interaction.  .NET remoting or WCF for example.  It will be more expensive as you probably make more round trips but it will work a lot better.
    Hans Passant.
    Wednesday, April 22, 2009 5:01 PM
    Moderator
  • Hmya, you traded one problem for another.  While you original approach was certainly very ugly, your new one solves some of the problems but created several new ones.  Hard ones, synchronizing threads is difficult.  I suspect the real issue here is your use of a named pipe to try to shuttle state between otherwise disconnected chunks of code.  Pipes are not very good at that, everything needs to be encoded.  Forcing you to maintain state awareness on either side of the pipe.  That's brittle.  Minor code changes tend to avalanche since a new state representation is required.  Errors are fatal when the stored state no longer agrees with actual state.
    Hrm.

    Well, so far Named Pipes is the only simple way to communicate between EXE's (separate processes) other than using TCP or some other such network protocol.  The encoding is not a problem at all as I've designed self aware Classes that handle all my Pipe interaction. 
    the Base pipe is an abstract class that has an internal field of type System.IO.Pipes.PipeStream.  That class has all the CheckCommand(), CheckMessage(), CheckReady(), etc methods which use a BufferStruct class in order to verify if a command is waiting in the PipeStream buffer.  Additionally the GetMessage/SendMessage handles the UTF8 encoding internally so by the time I reach the layer of abstraction for the PipeServer and PipeClient which inherit from my abstract pipe class, they are none the wiser.  Such things are simple with this method:
       Public Function WaitReady(ByVal Timeout As Integer) As Boolean
          Dim ts As TimeSpan
          Dim start As DateTime = Now
          _buf.ClearHeader()
          Do
             If Timeout > -1 Then ts = Now - start
             Application.DoEvents()
          Loop Until ((Timeout > -1) AndAlso (ts.Seconds > Timeout)) OrElse (Not Connected) OrElse CheckReady()
          Return PipeCommand = PipeCommandsEnum.CMD_READY
       End Function
    
    Which simply waits until a command is in the Buffer.  To Check if there is anything in the PipeStream waiting to be read is non-invasive so it doesn't really hurt (save for the time spent polling the buffer but so far pipes haven't been written to handle other means of determinations). 

    My Event Based pipe is actually a wrapper for the PipeClient and PipeServer classes, basically running a loop to determine if a Command has come through the pipe.  I decided to rework my original concept removing extraneous events and simply making it a solo-event system.

    When Command comes through - OnPipeCommand() triggers the event with all the pipe data. 
    when OnPipeCommand comes through, i evaluate PipeCommandEventArgs.HasResponse and then use the internal Event Argument data to formulate a response to the other end of the pipe. 

    So far this has been the only way I can easily communicate bettween two different applications that are running concurrently, so, I'll eventually get it to work right.  *chuckle*

    Jaeden "Sifo Dyas" al'Raec Ruiner



    "Never Trust a computer. Your brain is smarter than any micro-chip."
    Wednesday, April 22, 2009 6:36 PM
  • What happens in the calling code when this function returns False?  Does it loop too?  Or do you need a state machine that runs off a timer?  What happens when the user closes the UI?  She can, you'll let her by calling Application.DoEvents().  Does it crash?  Or does it just keep running without the user noticing?  What happens when you detect a timeout 1000 times in a row?  Does the program just hang?  Or does the application have to count and detect gross timeouts?

    At least with this code, you haven't done anything that .NET Remoting doesn't do better.  You ought to take a look at it.

    Hans Passant.
    Friday, April 24, 2009 4:16 PM
    Moderator
  • Hrm.

    Remoting seems somewhat like overkill, as I am not utilizing Web/Net communication. 

    Basically, it appears that for Remoting to work the Host (server) applicaiton has to be running concurrently in order to be aware of the request for the remotable type.  What my system is designed for is an auto update method (because I don't use click once, and most .net installers don't evaluate the revision only the maj/min/build of the version. 

    My system is pretty simple, using a share drive on our internal network, my application looks for specific command line parameters.  if it receives a specific set, it opens up an xml based DB from the share drive and displays all the projects that are included currently.  I can add more projects, or intuitively add the most recent version of an existing project (should a new version exist than what is stored in the db).  I then build the update which archives all appropriate files to a specific structure on the share drive and then closes out. 

    The same application is also copied into the EXE location for the project being updated, making building original installs very easy.  so MyProj.exe is compiled into an msi including the Update.exe.  When it loads, the splash screen pops up.  As the splash is displayed, it loads the Update.exe process with a /pipe=%pipename% parameter, and then starts a pipe server with the same name.  the update.exe sees the pipe parameter and starts its internal pipe communication, receiving version and file information from the server once connected, then loading up the DB from the sharedrive, and verifying versions, naturally performing a self-check first to see if the update.exe needs an update as well.  It then downloads the archive and applies the update as necessary, providing progress bar information via the pipe back to the splash screen for display. 

    My current setup is basically taking advantage of an already written NamedPipe handler that is very smooth and easy, I was trying to reduce the per application overhead by making it more event/command based. 

    Looking into the .Net Remoting i'm not exactly sure how I would effect the same effects.  I'd have to make every object and class and detailed whos-er-whats-it compatible with remoting, or at least come up with some way to serialize the data for communication via a generic remotable type.  I was trying to make the update process universal enough so all i'd need to do for each application is simply:

    SplashScreen_Shown()
      _serverPipe = New ServerPipe(Info.Version, AssInfo.Guid, APplicaiton.ExecutablePath)
               'AssInfo is a shared class i wrote that can get any assembly information from any assembly,
               'the default being the calling assembly.
       FormTimer.Start()
    end sub
     
    FormTimer_Tick()
    formTimer.Stop() 
    _serverPipe.Execute
    if _serverPipe.Restart then
       'handle restart code
    end if
    Me.Close()
    end sub

    _serverPipe_OnProgress()
       UpdateStatusLabel()
       UpdateProgressBar()
    end sub

    the Update exe would handle the rest, and all the restart info in the app being updated would do is close down and exit (via the Startup event from the My.Application) and the Update.exe would restart the process before it closes, using naturally the newly updated exe. 

    Of course the above code is grossly psuedo-fied but the idea is sound.  How this would be "simpler" with remoting will take me some time to investigate. 

    As for the user being able to close the applicaiton, well my code is written to simply tasks for me and anyone willing to use them.  I do not coddle the programmer.  there is naturally a FormClosing which can cancel the form closing operation, and thus in that event the programmer using this method of communication should most definitely check first with the pipe to determine its status.  I have never had a problem with such things, and will never coddle other programmers from having to think like that as well.  I prefer powerful code over safe code. *chuckle*

    I'll keep looking into remoting and hopefully other (non msdn) documentation can be more helpful in pointing out its feasability in this endeavor.

    Thanks
    Jaeden "Sifo Dyas" al'Raec Ruiner
    "Never Trust a computer. Your brain is smarter than any micro-chip."
    Friday, April 24, 2009 4:56 PM