|
-
Apr 3rd, 2011, 05:56 AM
#1
Thread Starter
Hyperactive Member
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.
-
Apr 3rd, 2011, 06:13 PM
#2
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.
-
Apr 3rd, 2011, 10:26 PM
#3
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:
private void someObject_SomeEvent(object sender, EventArgs e) { this.SomeMethod(); } private void SomeMethod() { if (this.InvokeRequired) { this.Invoke(new Action(SomeMethod)); } else { this.label1.Text = "SomeEvent was raised"; } }
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:
public class SomeClass { private readonly SynchronizationContext synchroniser = SynchronizationContext.Current; public event EventHandler SomeEvent; protected virtual void OnSomeEvent(EventArgs e) { if (this.SomeEvent != null) { this.synchroniser.Send(RaiseSomeEvent, e); } } private void RaiseSomeEvent(object eventArgs) { this.SomeEvent(this, (EventArgs) eventArgs); } }
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.
-
Apr 3rd, 2011, 11:04 PM
#4
Re: Custom Event, Cross-Threading problem
 Originally Posted by jmcilhinney
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 ]
-
Apr 3rd, 2011, 11:22 PM
#5
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.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|