Results 1 to 5 of 5

Thread: Custom Event, Cross-Threading problem

  1. #1

    Thread Starter
    Hyperactive Member Cyb3rH4Xter's Avatar
    Join Date
    May 2009
    Location
    Sweden
    Posts
    449

    Custom Event, Cross-Threading problem

    Geez this is my fifth thread in like 2 days...

    Anyway, the help I get here is awesome and I learn a lot so here is the problem:

    When I register a function for an event I created the function is run on another thread apparently.

    Code:
    Code:
            // delegate declaration
            public delegate void ChangingHandler(object sender, ProgressArgs pa);
    
            // event declaration
            public event ChangingHandler ProgressChanged;
            private void StartIt()
            {
                myworker.ProgressChanged += new Worker.ChangingHandler(myworker_ProgressChanged);
                myworker.Start();
    
            }
    
            private void myworker_ProgressChanged(object sender, ProgressArgs e)
            {
                progressBar.Value = e.Percentage();
            }
    It refuses to change the value on the progressbar because of that. If I do the same with a backgroundworker it works, why is my changed function executed on another thread and how do I make it execute on the main thread?
    Last edited by Cyb3rH4Xter; Apr 3rd, 2011 at 06:00 AM.

  2. #2
    PowerPoster Evil_Giraffe's Avatar
    Join Date
    Aug 2002
    Location
    Suffolk, UK
    Posts
    2,555

    Re: Custom Event, Cross-Threading problem

    The function that handles the event is always run on the thread that raised the event, it doesn't matter which thread registered that function.

    The BackgroundWorker raises its ProgressChanged event explicitly on the UI thread, which is why it works. (Um, actually, check that. I vaguely remember that sometimes you need to invoke onto the UI thread with a BW, although I may be wrong)

    Something somewhere in your code will need to invoke onto the UI thread.

  3. #3
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,222

    Re: Custom Event, Cross-Threading problem

    The event handler gets executed on whatever thread the event was raised on. As EG suggests, at some point, your code is going to have to marshal a method call to the UI thread to update the UI. Exactly how and where you should do that depends on the circumstances.

    Does the class raising the event implement the multi-threading itself, or is that done by the object using that class? Basically, whoever initiates the multi-threading should be responsibly for marshalling method calls to the UI thread.

    1. If it's a form that initiated the multi-threading then it's the form that should look after that operation. It can do so using its own ISynchronizeInvoke implementation. The object raising the event shouldn't have to know anything about threads at all. It simply raises the event and the form handles it. The form then marshals a method call to the UI thread, e.g.
    csharp Code:
    1. private void someObject_SomeEvent(object sender, EventArgs e)
    2. {
    3.     this.SomeMethod();
    4. }
    5.  
    6. private void SomeMethod()
    7. {
    8.     if (this.InvokeRequired)
    9.     {
    10.         this.Invoke(new Action(SomeMethod));
    11.     }
    12.     else
    13.     {
    14.         this.label1.Text = "SomeEvent was raised";
    15.     }
    16. }
    2. If it's the object raising the event that initiated the multi-threading then that object should ensure that it raises its event on the thread on which the object was created. It can do that using the SynchronizationContext class, e.g.
    csharp Code:
    1. public class SomeClass
    2. {
    3.     private readonly SynchronizationContext synchroniser = SynchronizationContext.Current;
    4.  
    5.     public event EventHandler SomeEvent;
    6.  
    7.     protected virtual void OnSomeEvent(EventArgs e)
    8.     {
    9.         if (this.SomeEvent != null)
    10.         {
    11.             this.synchroniser.Send(RaiseSomeEvent, e);
    12.         }
    13.     }
    14.  
    15.     private void RaiseSomeEvent(object eventArgs)
    16.     {
    17.         this.SomeEvent(this, (EventArgs) eventArgs);
    18.     }
    19. }
    The Send (synchronous) and Post (asynchronous) methods of the SynchronizationContext class will marshal the method call to whatever thread the Current property was originally accessed on to get the instance.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  4. #4
    PowerPoster Evil_Giraffe's Avatar
    Join Date
    Aug 2002
    Location
    Suffolk, UK
    Posts
    2,555

    Re: Custom Event, Cross-Threading problem

    Quote Originally Posted by jmcilhinney View Post
    Basically, whoever initiates the multi-threading should be responsibly for marshalling method calls to the UI thread.
    May I add a nuance to that reasoning?

    Since it is the UI that is imposing the requirement that all UI updates be done on the UI thread, I also find it acceptable for the Form to marshal onto the UI thread even if it was not the one that initiated the multi-threading.

    This does split the initiation/resolution of multi-threadedness, which may or may not be a problem from your point of view, but does simplify the marshalling because you don't have to sling SyncContexts about. As ever, YMMV.



    [Edit: However, Invoking onto the UI thread from the objects when it was the Form that started the Multi-threading? That's just plain dumb ]

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,222

    Re: Custom Event, Cross-Threading problem

    To add further to what EG said, it really does depend on the circumstances as to what is the best solution. We don't really know enough about the circumstances to say, so my definitive statement probably wasn't justified. There are various possibilities.

    1. You might want BackgroundWorker-like behaviour, where events are always raised on the same thread on which the object was created. In that case, use the SynchronizationContext in the class itself. My BackgroundMultiWorker class in the VB.NET CodeBank is an example.

    2. You might want the object to sometimes raise an event on whatever thread it is using internally and sometimes to raise an event on the thread that owns a specific control. For that, add your own SynchronisingObject property, much like the FileSystemWatcher and Timers.Timer do. You can then use this object to marshal internally if it is set.

    3. As EG said, you might just let the form handle the marshalling all the time. This is probably less appropriate if the form should remain ignorant of the multi-threading inside the other class but, if your types are already tightly-coupled, this little bit more won't hurt.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width