Difference in EventHandler syntax on new event (non generic) - c#

I'm trying to wrap my head around this problem. The below code ultimately merely counts the number of garbage collected objects of type SubscribersClass. When the code runs as shown, I get a SubscribersClass.Count value of 0. When I comment out the first line in EventsClass and uncomment the remainder of that class, the value of SubscribersClass.Count is 10.
The only thing I can come up with is that because there is something wrong with the EventsClass EventHandler (as shown), then no instances of SubscribersClass are actually being created.
Was hoping someone could help me to understand what is happening exactly.
This is an academic and of no practical value. Just tying to figure it out but have thus far only managed to get GoogleBlisters.
namespace understandingEvents
{
public class EventsClass
{
public event EventHandler SomeEvent; // if this is commented out and
// remainder of class is uncommented
// it works fine
/*
public event EventHandler SomeEvent
{
add
{
Console.WriteLine("An event has been added");
}
remove
{
Console.WriteLine("An event has been removed");
}
}
*/
}
public class SubscribersClass
{
static int count = 0;
static public int Count
{
get { return count; }
}
public SubscribersClass (EventsClass eventPublisher)
{
eventPublisher.SomeEvent += new EventHandler(Subscriber_SomeEvent);
}
~SubscribersClass()
{
Interlocked.Increment(ref count);
}
public void Subscriber_SomeEvent(object sender, EventArgs e)
{
Console.WriteLine("This is an event");
}
}
class Program
{
static void Main(string[] args)
{
EventsClass publisher = new EventsClass();
for (int i = 0; i < 10; i++)
{
SubscribersClass subscriber = new SubscribersClass(publisher);
subscriber = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(SubscribersClass.Count.ToString());
}
}
}

When you use a standard, compiler implemented event (public event EventHandler SomeEvent;), subscribing to the event causes a reference to the subscriber to be kept in the event, since the delegate refers to an instance method of the SubscribersClass.
This means that publisher is still holding references to each and every subscriber, so they are never released.
When you write your own add and remove handlers, you're not actually using the delegate, so the delegate is ignored (you'll find that raising the event has no effect and isn't handled by the subscribers in that case), which also means that those references aren't held, so the GC.Collect call can "clean up" the subscribers. This causes the count to increment.

When you subscribe to an event, the publisher will hold a reference to the subscriber. This is what allows the publisher to call all the methods that were subscribed to the event when the event takes place.
To solve your issue, simply remove the subscription before destroying the object. ( You'll need to store a reference to the publisher of the event). You can do this by manually calling some Unsubscribe method or (better) implement IDisposable and unsubscribe in Dispose().
public SubscribersClass (EventsClass eventPublisher)
{
m_eventPublisher = eventPublisher;
eventPublisher.SomeEvent += new EventHandler(Subscriber_SomeEvent);
}
public override void Dispose()
{
m_eventPublisher.SomeEvent -= Subscriber_SomeEvent;
}

You are not removing event susbscribers in your test code, as result the SubscribersClass instances are not getting collected.
Commented out code does not at all add listeners, so all instances of SubscribersClass are ready for GC as soon as they are created.
Note that code (when properly fixed) will also behave differently in DEBUG build due to extended lifetime of all variables. Consider putting all interesting code in a function and triggering GC outside it.

Related

Event handler affects in garbage collection in CLR

I'm completely confused about how event handler can affect in garbage collection operation.
For example, why object a1 not collected by garbage collection ( the destructor of a1 is not calling):
Even after unsubscribing timeChange eventHandler the destructor isn't called by garbage collector.
Best regards.
public class B
{
private void button1_Click(object sender, EventArgs e)
{
A a1 = new A();
a1.timeChange += A1_timeChange;
a1.Start();
a1 = null;
GC.Collect();
}
private void A1_timeChange(object sender, EventArgs e)
{
MessageBox.Show(((DateTime)sender).ToString() );
}
}
public class A
{
~A()
{
MessageBox.Show("A Collected");
}
public void Start()
{
if (timeChange != null)
{
Task.Factory.StartNew(() => {
while (true)
{
timeChange(DateTime.Now, null);
System.Threading.Thread.Sleep(3000);
}
});
}
}
public event EventHandler timeChange;
}
In summary
It is not the event itself that is causing this, but rather referencing an instance member of class A from a long running thread using a closure.
The event itself is not the issue, or is it?
This code a1.timeChange += A1_timeChange; causes a delegate inside class A that implements the event public event EventHandler timeChange to reference A1_timeChange inside class B.
So, the reference is the other way around
In your scenario, if you got rid of all references to class B, but did not unsubscribe from the event, then the event handler in A that points to a handler method in B could keep class B reachable, and therefore not GC'ed
What is really happening
Your class A is accessible from one of the GC roots (specifically, actively running threads), it's still reachable and therefore, not collected - read more
You have spawned an eternally running task (well it will stop when the app closes / foreground thread terminates) with this code
Task.Factory.StartNew(() => {
while (true)
{
timeChange(DateTime.Now, null);
System.Threading.Thread.Sleep(3000);
}
The thing is, that you're using a lambda that closes over the timeChange event, and that makes a compiler to generate a class that simply references class A
One more thing, just by having a descructor (which is compiled to a finalizer) you prolong the lifetime of your object by one GC collection - on the first GC, your object will be marked as unreachable, and will be put onto finalization queue. And on the next GC, the finalizer will actually run - read more

how to detect event handler and released it?

Suppose I have a class say a viewmode class mvvm. Then there are some event handlers created for this vm. then it could be used by many others with different situation.
So if I have an instance of myvm, I want to detect if there is any event handler hooked up and want to release it for memory issue.
What's the generic way to do this out of myvm, for example, I may not have the source code of myvm?
Events are designed such that code outside the class that declared them cannot get access to the underlying delegate. For example, according to Section "10.8 Events" in the C# Language specification (emphasis mine):
In an operation of the form x += y or x -= y, when x is an event and
the reference takes place outside the type that contains the
declaration of x, the result of the operation has type void (as
opposed to having the type of x, with the value of x after the
assignment). This rule prohibits external code from indirectly
examining the underlying delegate of an event.
Therefore, finding out what is subscribed to the event outside the class may be, at best, a "work around".
If you have access to the source of the class containing the event and you want to keep track of delegates hooked up to an event, implement the add and remove keyword in the event definition and manually keep track of them in a Dictionary.
If I understand you correctly.
This class wraps the unknown myvm class which I use SocketAsyncEventArgs to illustrate, cos obviously we don't have the source code for SocketAsyncEventArgs class.
And I wrapped the Completed event of SocketAsyncEventArgs class. When that event is triggered, _instance_Completed will be fired, then _myvm event will be fired. So what we need to do is subscribe/unsubscribe _myvm event.
Then I leave an event for people to subscribe/unsubscribe _myvm event, as subscribing/unsubscribing, the delegates are stored into a List therefore you can clear the
by call the ClearEvents() method.
Hope it will help.
public class WrapperClass
{
private EventHandler<SocketAsyncEventArgs> _myEvent;
private SocketAsyncEventArgs _myvm;
private List<Delegate> delegates;
public WrapperClass()
{
delegates = new List<Delegate>();
}
public void SetInstance(SocketAsyncEventArgs myvm)
{
_myvm = myvm;
_myvm.Completed += new EventHandler<SocketAsyncEventArgs>(_instance_Completed);
}
private void _instance_Completed(object sender, SocketAsyncEventArgs e)
{
if (_myEvent != null)
{
_myEvent(sender, e);
}
}
public event EventHandler<SocketAsyncEventArgs> myEvent
{
add
{
delegates.Add(value);
_myEvent = (EventHandler<SocketAsyncEventArgs>)Delegate.Combine(_myEvent, value);
}
remove
{
delegates.Remove(value);
_myEvent = (EventHandler<SocketAsyncEventArgs>)Delegate.Remove(_myEvent, value);
}
}
public void ClearEvents()
{
foreach (var d in delegates)
{
_myEvent = (EventHandler<SocketAsyncEventArgs>)Delegate.Remove(_myEvent, d);
}
}
}

Can events be used in a multi-threaded application safely

In the following code I have two classes, one that runs in a separate thread and fires events, the other that subscribes to this event and receives data from the event. The event code I have based off of Jon Skeet's article http://csharpindepth.com/Articles/Chapter2/Events.aspx
In this article http://www.codeproject.com/Articles/37474/Threadsafe-Events it says...
For this reason, I recommend the same approach that Jon Skeet ends up recommending at the end of Delegates and Events: "don't do that", i.e., don't use events in a multithreaded fashion. If an event exists on an object, then only one thread should be able to subscribe to or unsubscribe from that event, and it's the same thread that will raise the event.
Now obviously my design breaks that, in that it fires the event on a different thread to that it was subscribed on. How could I modify my design so that it adhears to the principle of not using events in a multi-threaded fashion or is this not possible ?
The other way I thought of doing it was just to pass in my callback method as a delegate into class B and call that instead of calling the event?
I may have completely the wrong end of the stick, so any clarification would be appreciated.
Note: I am aware that .Net 4.0 has apparently solved this issue, however I would still be interested in if there is a way to do it pre .Net 4
public delegate void MyDelegate(int a);
class A
{
void main()
{
B bObject = new B();
bObject.MyEvent += new MyDelegate(NiceMethod);
bObject.Run();
}
void NiceMethod(int a)
{
Console.Writeline({0}, a);
}
}
class B
{
readonly object eventLock = new object();
MyDelegate myDel;
public event MyDelegate MyEvent
{
add
{
lock (eventLock)
{
myDel += value;
}
}
remove
{
lock (eventLock)
{
myDel -= value;
}
}
}
//Assume this runs in a new thread and calls back data using MyEvent
//Have ommited thread code for simplicity
public void Run()
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
MyDelegate handler;
lock (someEventLock)
{
handler = myDel;
}
if (handler != null)
{
handler (i);
}
}
}
}
There isn't anything wrong about raising events or listening to events from different threads. It is the responsibility of the listener to deal with being invoked from another thread. As Marc Gravell notes in his comment, adding and removing of listeners to and from events from different threads is (and always has been) supported by the compiler generated add and remove implementations. The only problem is to raise the event in a thread safe fashion, which can be done by synchronizing the access to event via the same kind of spinlock the generated add and remove are using:
class B
{
public event MyDelegate MyEvent;
protected OnMyEvent(int p_Arg)
{
// Delegates are immutable and add/remove default
// implementations always generate a new instance of the
// delegate. Therefore, tTmp (if not null) can be safely invoked
var tTmp =
System.Threading.Interlocked
.CompareExchange(ref MyEvent, null, null);
if (tTmp != null) {
tTmp(p_Arg);
}
}
//Assume this runs in a new thread and calls back data using MyEvent
//Have ommited thread code for simplicity
public void Run()
{
for (int i = 0; i < 100; i++)
{
OnMyEvent(i);
}
}
}
The only thing that could happen is that the listener is invoked after it has been removed from the event list. IMHO, the listener must be able to deal with this situation as it deals with beeing invoked from separate threads...

Assigning pointer to event for use later

This is abit difficult to word, so I am going to rely mostly on code.
BTW if you can word the question in a better light please dont hesitate giving your 2c!
class CustomEventArgs : EventArgs
{
public delegate void CustomEventHandler( Object sender, CustomEventArgs args );
public int data;
public CustomEventArgs (int _data)
{
data = _data;
}
}
This is the event that we will be using in this example.
class EventGenerator
{
public event CustomEventArgs.CustomEventHandler WeOccasion;
public EventGenerator ()
{
Task.Factory.StartNew( () =>
{
var index = 1;
// just loop and generate events every now and then
while (true)
{
Thread.Sleep( 1000 );
WeOccasion( this, new CustomEventArgs (++index));
}
});
}
}
This class just loops through firing off CustomEventHandler events.
class EventActivity
{
// EventActivity has an event of the same type as EventGenerator's
public event CustomEventArgs.CustomEventHandler WeOccasion;
// this is the part I cant seem to get right
public event CustomEventArgs.CustomEventHandler Source ( get; set; }
public bool Active {
set
{
if (value)
{
Source += DoWork;
}
else
{
Source -= DoWork;
}
}
}
private void DoWork( Object sender, CustomEventArgs frame);
}
Here is where I really need help. I want almost a pointer to an event in an another class of type CustomEventHandler that I can later assign event handlers to when I activate the activity.
Here is a usage example wrapped in a class;
class EventAssigner
{
EventGenerator Generator;
EventActivity DoSomeThing1;
EventActivity DoSomeThing2;
public EventAssigner ()
{
// init
Generator = new EventGenerator();
DoSomeThing1 = new EventActivity();
DoSomeThing2 = new EventActivity();
// assign sources
DoSomeThing1.Source = Generator.WeOccasion;
DoSomeThing2.Source = DoSomeThing1.WeOccasion;
// activate the first activity
DoSomeThing1.Active = true;
}
public void Activate2()
{
// activate the second activity
DoSomeThing2.Active = true;
}
public void Deactivate2()
{
// deactivate the second activity
DoSomeThing2.Active = false;
}
}
Obiously this code doesnt work, and I suppose thats what I am asking. Can you get this design pattern to work?
What you're asking to do isn't really possible with .NET events, and probably isn't as desirable as you might think. A bit of background should help explain why:
Properties have a basic pattern with get and set operations. These are invoked by accessing the property (for a get) and an assignment to the property (for a set):
var x = instance.Prop1; // access
instance.Prop1 = x; // assignment
When you access an event from outside the class (i.e. instance.Event) you are given the "public" face, which, like properties, has two operations: add handler and remove handler. These are invoked using the += and -= operators.
instance.Event += this.Handler; // add
instance.Event -= this.Handler; // remove
The important thing to notice that it doesn't have a "get" operation - there is no way to get a reference to the event outside the class; you can only modify the handlers registered.
When you access an event from within a class, you are given the "private" face, which is essentially a special collection of delegates (function pointers) to the registered event handlers. When you invoke the delegate, you're actually asking the framework to iterate through the registered event handlers and invoke those.
if(this.Event != null)
{
this.Event.Invoke(e, args); // raise event
}
This separation of public face and private face is what allows you have a nice simple event keyword which magically gives you an event. It is also what stops you passing a reference to the event around.
To pass the event into registration methods, you have to pass the object the event is attached to. If you have multiple classes which implement the same event and you want to register them all in the same way, you should have them implement an interface with the event (yes, events can be on interfaces) and write your method to accept the interface as an argument.
If I'm reading you correct, you want the line
DoSomeThing1.Source = Generator.WeOccasion;
to save the pointer to the WeOccasion event, so that you can add the DoWork call to it later, right?
I don't think that is possible with "normal" code, as the event is not a value, but rather like a property. Consider the following analogous code:
myProp = aPerson.Name; // attempt to save the name property for later
myProp = "Fred"; // intent is to set aPerson.Name = "Fred"
If you want this to work I'd suggest using reflection to find the event, and add to it using the EventInfo.AddEventHandler method (http://msdn.microsoft.com/en-us/library/system.reflection.eventinfo.addeventhandler.aspx)

Do I need to remove this sort of event handler?

If I create a .NET class which subscribes to an event with an anonymous function like this:
void MyMethod()
{
Application.Current.Deactivated += (s,e) => { ChangeAppearance(); };
}
Will this event handler be a root keeping my class from being garbage collected?
If not, wohoo! But if so, can you show me the removal syntax? Just using -= with the same code seems wrong.
You can either use a "real" method as Simon D. suggests, or do this:
EventHandler handler = null;
handler = (s,e) => {
Application.Current.Deactivated -= handler;
ChangeAppearance();
};
Application.Current.Deactivated += handler;
Since this is somewhat ugly and defeats the purpose of using a lambda to be succint, I 'd probably go with refactoring it out to a method. But it's useful to know what else works.
Warning: It goes without saying that you have to be super ultra careful to not mess with the value of handler at all between the point where you subscribe to the event and the point where it is actually invoked, otherwise the unsubscribe part won't work correctly.
I think you do need to unsubscribe, because the event provider (the application) lives - or at least can live - longer than your consumer. So each subscribing instance dying while the app remains living will create memory leaks.
You are subscribing an anonymous delegate to the event.
This is why you can't unsubscribe it the same way, because you cannot address it anymore.
Actually you are creating the method at the same moment you subscribe, and you don't store any pointer to the newly created method.
If you slightly change your implementation to use a "real" method, you can easily unsubscribe from the event the same way you subscribe to it:
Application.Current.Deactivated += ChangeAppearance;
Application.Current.Deactivated -= ChangeAppearance;
private void ChangeAppearance(object sender, EventArgs eventArgs)
{
throw new NotImplementedException();
}
You definitely have to clean up the reference. If there were any doubt you could easily test with your own static event like so.
static class MemoryLeak
{
static List<Action<int>> list = new List<Action<int>>();
public static event Action<int> ActivateLeak
{
add
{
list.Add(value);
}
remove
{
list.Remove(value);
}
}
}
Then by setting a breakpoint in the remove function you can see that your reference is not cleaned up.
class Program
{
static void Main(string[] args)
{
foo f = new foo();
MemoryLeak.ActivateLeak += o => f.bar();
f.tryCleanup();
}
}
class foo
{
public void bar()
{ }
public void tryCleanup()
{
MemoryLeak.ActivateLeak -= o => bar();
}
}
As an alternative to Simon's solution you could use a second closure to create a "detach" action which can be passed around.
foo f = new foo();
Action<int> callfoo = o => f.bar();
MemoryLeak.ActivateLeak += callfoo;
Action cleanUp = () => MemoryLeak.ActivateLeak -= callfoo;
// Now you can pass around the cleanUp action and call it when you need to unsubscribe from the event.
cleanUp();
This is indeed a leak that prevents garbage collection. There are ways around it - with WeakReference being one of the better ones.
This link has a good conversation and good answers for you.

Categories

Resources