This question already has answers here:
C# Events and Thread Safety
(15 answers)
Closed 9 years ago.
So i've read around that instead of calling a event directly with
if (SomeEvent != null)
SomeEvent(this, null);
i should be doing
SomeEventHandler temp = SomeEvent;
if (temp != null)
temp(this, null);
Why is this so? How does the second version become thread safe? What is the best practice?
IMO, the other answers miss one key detail - that delegates (and therefore events) are immutable. The significance of this is that subscribing or unsubscribing an event handler doesn't simply append/remove to a list - rather, it replaces the list with a new one with an extra (or one less) item on it.
Since references are atomic, this means that at the point you do:
var handler = SomeEvent;
you now have a rigid instance that cannot change, even if in the next picosecond another thread unsubscribes (causing the actual event field to become null).
So you test for null and invoke it, and all is well. Note of course that there is still the confusing scenario of the event being raised on an object that thinks it unsubscribed a picosecond ago!
Events are really syntactic sugar over a list of delegates. When you invoke the event, this is really iterating over that list and invoking each delegate with the parameters you have passed.
The problem with threads is that they could be adding or removing items from this collection by subscribing/unsubscribing. If they do this while you are iterating the collection this will cause problems (I think an exception is thrown)
The intent is to copy the list before iterating it, so you are protected against changes to the list.
Note: It is however now possible for your listener to be invoked even after you unsubscribed, so you should make sure you handle this in your listener code.
Best practice is the second form. The reason is that another thread might null or alter SomeEvent between the 'if' test and the invocation.
Here is a good write up about .NET events and race conditions with threads. It covers some common scenarios and has some good references in it.
Hope this helps.
Related
So if I do something like this on my event declaration:
public event MyDelegate MyEvent = delegate { };
I read that this makes null checks redundant, that there will be a no-op subscriber.
I'm curious how this works under the hood. What exactly happens and is this a good practice, or should I stick with usual null check before firing the event handler?
The null checks aren't tricky because of an extra if - they are tricky because they're unreliable in a multi-threaded environment.
It's a trade-off. If you expect that the event will only have 0-1 delegates, using a (safe) null-check is probably better. If you expect to have multiple subscribers that keep subscribing and unsubscribing, you're better off with the "null-delegate". It does mean an extra delegate in the call chain, but that's usually cheap enough for most uses of events in .NET.
You wouldn't want to use it on e.g. a control that has 50 different events most of which never have a subscriber, or your behaviour changes based on whether there is a subscriber or not.
As for what happens under the hood, well... nothing. This does no magic whatsoever. The trick is in how += works on events / delegates.
If you have a null event, and use +=, it simply assigns a new delegate to the event. If there already is a delegate, it will create a new delegate, which is a combination of the previous delegate and the new one. If you use that null-delegate, it simply means that there is always some delegate to combine. The usual code for invoking an event can look something like this:
var ev = Click;
if (ev != null) ev();
This means that if there is no subscriber to the event, there is no invocation. In the null-delegate case, the code is further simplified to
Click();
This always means a delegate invocation - at the very least, your null-delegate is invoked. Sure, it doesn't actually do anything, but you keep the delegate invocation overhead. Again, this is rarely a problem, but there are cases where this is unacceptable.
I have the following code to let the GUI respond to a change in the collection.
myObservableCollection.CollectionChanged += ((sender, e) => UpdateMyUI());
First of all is this a good way to do this?
Second: what's the code to unsubscribe from this event? Is it the same but with -= (and then the complete anonymous method again)?
First of all... yes its a good way of doing it, it's clean, small form and easy to read & understand... the caveat of course is "Unless you later want to unsubscribe".
I believe Jon Skeet pointed out before that
"the specification explicitly doesn't guarantee the behaviour either way when it comes to equivalence of delegates created with anonymous methods."
So if you need to unsubscribe from the event at a later time, you'd be best to actually create a delegate instance so you can hang onto the reference for later.
var myDelegate = delegate(sender, e){UpdateMyUI()};
myObservableCollection.CollectionChanged += myDelegate;
myObservableCollection.CollectionChanged -= myDelegate;
If you need to unsubscribe from an event, you need an instanced reference. Unfortunately, that means you can't use that particular syntax.
It's an ok way to go, unless myObservableCollection is going to live longer than 'this', in which case you could end up with a memory leak, as the delegate which is created behind the scenes will conserve a reference to your 'this', which will keep it alive. If you are repeatedly creating and 'destroying' whatever is listening to the event, you will find that they aren't being collected by the garbage collector.
If this is a problem, you can go the route suggested in the answers, conserving a reference to the handler, which you must create first.
Another solution is to use weak references to create an event handler which will allow the subscriber to be collected if there are not other references. I've explored this solution in this question and answer.
Most code I have seen uses the following way to declare and invoke event firing:
public class MyExample
{
public event Action MyEvent; // could be an event EventHandler<EventArgs>, too
private void OnMyEvent()
{
var handler = this.MyEvent; // copy before access (to aviod race cond.)
if (handler != null)
{
handler();
}
}
public void DoSomeThingsAndFireEvent()
{
// ... doing some things here
OnMyEvent();
}
}
Even ReSharper generates an invoking method the way mentioned above.
Why not just do it this way:
public class MyExample
{
public event Action MyEvent = delegate {}; // init here, so it's never null
public void DoSomeThingsAndFireEvent()
{
// ... doing some things here
OnMyEvent(); // save to call directly because this can't be null
}
}
Can anyone explain a reason why not to do this? (pro vs. cons)
The pros and cons are:
null checks are extremely cheap; we're talking billionths of a second. Allocating a delegate and then garbage collecting it unsuccessfully for the rest of the lifetime of the object could take maybe upwards of a few millionths of a second. Plus, you're consuming dozens of bytes more memory. Plus, every time the event fires, you get an unnecessary call to a method that does nothing, consuming even more microseconds. If you're the sort of person who cares about millionths of a second and dozens of bytes then that might be a meaningful difference; for the vast majority of cases it will not.
You have to remember to always create the empty delegate. Is that really any easier than remembering to check for null?
Neither pattern actually makes the event threadsafe. It is still entirely possible with both patterns for an event handler to be fired on one thread while being removed on another thread, and that means that they race. If your handler removal code destroys state that the handler needs, then it is possible that one thread is destroying that state while another thread is running the handler. Do not think that merely checking for null or assigning an empty handler magically eliminates race conditions. It only eliminates the race condition that results in dereferencing null.
It is a style thing for sure; however, I think most developers go for safety over style when it comes to null checks. There is nothing in this world that can guarantee that a bug will not creep into the system and that null check will continue to be unnecessary.
It can still be null. Consider:
var x = new MyExample();
x.MyEvent += SomeHandler;
// ... later, when the above code is disposed of
x.MyEvent -= SomeHandler;
EDIT: actually, I take it back. Having tested this, if you've used an anonymous delegate to set the handler, it doesn't look like you can clear it, so you can save the null check.
I'm not sure whether this is reliable behaviour though, or just an artifact of the language implementation...
The best tradeoff I've seen for simplifying event firing is the addition of an extension method. See Raising C# events with an extension method - is it bad?.
I guess C# eventhandler has a list of listeners and it loops thru the list when sending a message. My question is how does this works in internally. Does it make a copy of the list before looping it thru and if so, what happens if someone unregister itself after the list has been copied but it has not yet received the message.
Will it still get the message even do it has unregister itself?
A delegate is immutable, so when you are invoking a delegate the list of subscribers is known and fixed. Subscribing or unsubscribing replaces the delegate that underpins the event.
This does indeed mean that in a multi-threaded scenario you can receive an event after unsubscribing, because either:
the delegate was already in the process of being invoked
a snapshot of the delegate had already been obtained for the purpose of invoking
by 2, I mean the usual pattern (to prevent a null-ref during invoke):
var handler = SomeEvent;
// <===== another thread could unsubscribe at this point
if(handler != null) handler(sender, args); // <== or part way through this invoke
// (and it either case, have the event trigger even though they think they have
// unsubscribed)
For that reason, if you are coding complex multi-threaded code with events, you should code defensively such that the event firing after you think you have unsubscribed is not a problem.
These nuances do not really impact single-threaded code.
I have the following code to let the GUI respond to a change in the collection.
myObservableCollection.CollectionChanged += ((sender, e) => UpdateMyUI());
First of all is this a good way to do this?
Second: what's the code to unsubscribe from this event? Is it the same but with -= (and then the complete anonymous method again)?
First of all... yes its a good way of doing it, it's clean, small form and easy to read & understand... the caveat of course is "Unless you later want to unsubscribe".
I believe Jon Skeet pointed out before that
"the specification explicitly doesn't guarantee the behaviour either way when it comes to equivalence of delegates created with anonymous methods."
So if you need to unsubscribe from the event at a later time, you'd be best to actually create a delegate instance so you can hang onto the reference for later.
var myDelegate = delegate(sender, e){UpdateMyUI()};
myObservableCollection.CollectionChanged += myDelegate;
myObservableCollection.CollectionChanged -= myDelegate;
If you need to unsubscribe from an event, you need an instanced reference. Unfortunately, that means you can't use that particular syntax.
It's an ok way to go, unless myObservableCollection is going to live longer than 'this', in which case you could end up with a memory leak, as the delegate which is created behind the scenes will conserve a reference to your 'this', which will keep it alive. If you are repeatedly creating and 'destroying' whatever is listening to the event, you will find that they aren't being collected by the garbage collector.
If this is a problem, you can go the route suggested in the answers, conserving a reference to the handler, which you must create first.
Another solution is to use weak references to create an event handler which will allow the subscriber to be collected if there are not other references. I've explored this solution in this question and answer.