.NET对象的事件和处置/ GC对象、事件、NET、GC

2023-09-04 00:05:43 作者:时光如槿夏微凉

编辑:后,乔尔Coehoorns优良的答案,我知道我需要更加具体的,所以我修改了code更接近我想了解的事...

活动:据我了解,在后台的事件是事件处理器又名代表时引发的事件将被执行的收藏。所以对我来说,这意味着,如果对象的是活动E和对象X订阅事件的 YE 和是将有参考X,因为是必须执行位于X中的方法,这样一来, X不能收集,这件事我明白了。

  //创建参照本(B)中。
a.EventHappened + =新的EventHandler(this.HandleEvent);
 

但是这并不是乔尔Coehoorn告诉...

  

不过,有一个问题与事件,有时人们喜欢用IDisposable接口与有事件的类型。的问题是,当X型预订在另一个Y型事件中,X现在有一个参考到Y此引用将prevent Y均从收集

我不明白的 X 将引用在Y ???

我修改了一下我的例子来说明我的情况更接近:

 类服务//让我们说,这是Windows服务,必须全天候在线
{
    一_a;

    无效启动()
    {
       CustomNotificationSystem.OnEventRaised + =新的EventHandler(CustomNotificationSystemHandler)
       _a =新的A();

       乙B 1 =新的B(_a);
       乙B2 =新的B(_a);
       ÇC1 =新C(_a);
       ÇC2 =新C(_a);
    }

    无效CustomNotificationSystemHandler(参数)
    {

        // _ a.Dispose();添加的**编辑2 ***
        a.Dispose();

        _a =新的A();
        / *
        B1,B2,C1,C2将继续存在的是,我知道他们现在订阅
        _a以previous实例,它的行由我,但在这个例子中,现在,没人
        引用$ P $ _a的pvious实例(二不持有引用_a)和我
        理论上,_a的previous例如,现在可以收集...还是我缺少
        什么???
        * /
    }

}

A类:IDisposable的
        {
           公共事件的EventHandler EventHappened;
        }

        B类
        {
           公众B(A一)// B类不存储引用内部。
           {
              a.EventHappened + =新的EventHandler(this.HandleEventB);
           }

           公共无效HandleEventB(对象发件人,EventArgs参数)
           {
           }
        }

        C类
        {
           市民C(A中)// B类店面不大引用内部。
           {
              a.EventHappened + =新的EventHandler(this.HandleEventC);
           }

           公共无效HandleEventC(对象发件人,EventArgs参数)
           {
           }
        }
 

编辑2:确定,现在很明显,当用户预订出版商的事件,它的不可以创建一个参考用户发布。为用户创建(通过事件处理程序)只能从出版商的参考......在这种情况下,当发行人收集的GC之前的用户(用户寿命大于出版社),没有任何问题。

但 ...我所知,它不能保证当GC将收集的出版商因此从理论上讲,即使用户寿命大于出版商,它可能发生的用户是合法的收集,但是出版商仍然没有收集(我不知道如果在最接近的GC周期,GC将是足够聪明的,然后再收集发布者的用户。

总之,在这种情况下,因为我的用户不必直接引用出版商和无法取消的情况下,我想使出版商实现IDisposable,为了处理前删除他的所有引用(见在CustomNotificationSystemHandler在我的例子)。

,再次我应该在出版商写文章是为了清除给用户的所有引用处置方法?它应该被EventHappened - =无效;或EventHappened = NULL;或者有没有办法做到这一点在这样的方式,我需要做类似下面???

 公共事件的EventHandler EventHappened
   {
      加
      {
         事件表[EventHappened] =(事件处理程序)事件表[EventHappened] +价值;
      }
      去掉
      {
         事件表[EventHappened] =(事件处理程序)事件表[EventHappened]  - 值;
      }
   }
 
ADO.Net四个对象小结

解决方案

我在你的样品code加了我的意见。

  A级:IDisposable的
{
   公共事件的EventHandler EventHappened
   {
      加
      {
         事件表[EventHappened] =(事件处理程序)事件表[EventHappened] +价值;
      }
      去掉
      {
         事件表[EventHappened] =(事件处理程序)事件表[EventHappened]  - 值;
      }
   }

   公共无效的Dispose()
   {
      //艾米特:如果你只有一个事件EventHappened,
      //你可以清理用户如下

      事件表[EventHappened] = NULL;

      //艾米特:EventHappened = NULL不会在这里工作,因为它是
      //只是一个语法糖清编译器生成的后盾委托。
      //既然你已经增加了添加和删除没有编译器生成
      //委托清除
      //
      //以上只是来解释这个概念。
      //如果事件表为事件处理的字典
      //你可以简单的调用就可以了清除。
      //这会工作,即使有更多的像EventHappened事件
   }
}

B类
{
   公众B(A一)
   {
      a.EventHappened + =新的EventHandler(this.HandleEventB);

      //你是绝对正确的位置。
      // B类不存储任何引用
      //订阅事件中不添加任何引用发行
      //这里你正在做的是叫'添加'的'EventHappened'方法
      //传递一个委托持有引用B.
      //因此,有从A路径到B,但不能逆转。
   }

   公共无效HandleEventB(对象发件人,EventArgs参数)
   {
   }
}

C类
{
   市民C(A中)
   {
      a.EventHappened + =新的EventHandler(this.HandleEventC);
   }

   公共无效HandleEventC(对象发件人,EventArgs参数)
   {
   }
}

一流的服务
{
    一_a;

    无效启动()
    {
       CustomNotificationSystem.OnEventRaised + =新的EventHandler(CustomNotificationSystemHandler)

       _a =新的A();

       //艾米特:你说得对这些不存储任何引用_a
       乙B 1 =新的B(_a);
       乙B2 =新的B(_a);
       ÇC1 =新C(_a);
       ÇC2 =新C(_a);
    }

    无效CustomNotificationSystemHandler(参数)
    {

        //艾米特:你决定_a一直住其生命,必须进行处理。
        //在这里,我想你要处理,使其停止射击的事件
        //更多关于这个版本
        _a.Dispose();

        //艾米特:现在_a指向一个全新的A和因此previous实例
        //可用于收集,因为有没有活动引用
        _a现在// previous
        _a =新的A();
    }
}
 

  

B1,B2,C1,C2将继续存在的是,我知道他们现在会   订阅_a的previous实例,它的行由我,但在   例如,现在,没有人引用_a的previous实例(二不   持有参考_a)和我的理论,_a的previous例如,现在   可收集...还是我失去了一些东西???

通过我的意见,在上述code解释说,你是不是缺少什么位置:)

  

但是...我所知,它不能保证时,GC将收集   发行人因此从理论上讲,即使用户寿命大于   出版商,可能发生的是用户是合法的集合,但   出版商仍然没有收集(我不知道如果在最近的GC   周期,GC将是足够聪明的,然后再收集发行   订阅者。

由于出版商引用用户,它永远不可能发生,用户成为出版商之前有资格领取,但反向可以实现的。如果发布者被用户之前收集的话,像你说的,是没有问题的。如果用户属于低GC一代比发行则由于发行持有引用用户,GC会将用户的访问,并不会收集。如果两个属于同一代,它们将被收集在一起。

  

因为我的用户不必直接引用出版商和   不能退订的情况下,我想使出版商   实现IDisposable

相反的是一些建议,我建议如果实施,在任何时候,你是确定性确信对象不再需要处置。只需更新对象引用可能并不总是导致对象停止发布活动。

考虑以下code:

 类MainClass
{
    公共静态出版社出版;

    静态无效的主要()
    {
        出版商=新发行();

        螺纹eventThread =新主题(DoWork的);
        eventThread.Start();

        Publisher.StartPublishing(); //继续触发事件
    }

    静态无效的DoWork()
    {
        VAR用户=新用户();
        用户= NULL;
        //用户通过出版商的SomeEvent只引用
        Thread.sleep代码(200);
        //我们已经等了,我们不要求发布者现在
        出版商= NULL;
        所以GC.Collect();
        //即使GC.Collect的,发行的时候,我们已经设置发布为空,不收集甚至
        //这是因为'StartPublishing'的方法是在执行中,在该时间点
        //这意味着它是从主线程的堆栈隐式访问(通过'this指针)
        //这也意味着,订户保持存活
        //即使我们打算发布停止发布,它会不断发射,由于有些隐藏的提到它从主线程事件!!!!
    }
}

内部类出版社
{
    公共无效StartPublishing()
    {
        Thread.sleep代码(100);
        InvokeSomeEvent(空);
        Thread.sleep代码(100);
        InvokeSomeEvent(空);
        Thread.sleep代码(100);
        InvokeSomeEvent(空);
        Thread.sleep代码(100);
        InvokeSomeEvent(空);
    }

    公共事件的EventHandler SomeEvent;

    公共无效InvokeSomeEvent(对象E)
    {
        事件处理程序处理程序= SomeEvent;
        如果(处理!= NULL)
        {
            处理程序(这一点,NULL);
        }
    }

    〜出版商()
    {
        Console.WriteLine(我从来没有印刷);
    }
}

内部类用户
{
    公众用户()
    {
        如果(MainClass.Publisher!= NULL)
        {
            MainClass.Publisher.SomeEvent + = PublisherSomeEvent;
        }
    }

    无效PublisherSomeEvent(对象发件人,EventArgs的)
    {
        如果(MainClass.Publisher == NULL)
        {
            //如何能空触发一个事件!引发异常
            抛出新的异常(Booooooooommmm);
            //但是请注意发件人不为空
        }
    }
}
 

如果你运行上面的code,往往不是你会收到Booooooooommmm。因此想法是,事件发布者必须停止射击的事件时,我们相信,它的生命就到了。

这可以通过Dispose方法来实现。

有两种方法来实现这一点:

设置一个标志IsDisposed和射击任何事件之前检查它。 清除了事件的用户列表(如我在你的code意见建议)。

2的好处是,你释放任何对用户,从而使那里收集(正如我刚才解释,即使发布者是垃圾,但属于更高世代的话,就可能仍然延长征收较低的代用户)。

不过,无可否认,这将是相当罕见的,你遇到的证明的行为,由于发行的隐藏可达性,但是你可以看到2个好处是明确的,适用于所有事件发布者特别是长期居住的人(单身任何人!)。这本身就是值得实现Dispose和去2。

EDIT: After Joel Coehoorns excellent answer, I understand that I need to be more specific, so I modified my code to be closer to thing I'm trying to understand...

Events: As I understand, in the background the events are "collection" of EventHandlers aka Delegates which will be executed when event raised. So for me it means that if object Y has event E and object X subscribes to event Y.E, then Y will have reference to X, since Y must execute the method located in X, in that way, X can not be collected, and that thing i understand.

//Creates reference to this (b) in a.
a.EventHappened += new EventHandler(this.HandleEvent);

But it is not what Joel Coehoorn tells...

However, there is an issue with events such that sometimes people like to use IDisposable with types that have events. The problem is that when a type X subscribes to events in another type Y, X now has a reference to Y. This reference will prevent Y from being collected.

I not understand how X will reference the Y ???

I modified a bit my example to illustrate my case more closer:

class Service //Let's say it's windows service that must be 24/7 online
{       
    A _a;

    void Start()
    {
       CustomNotificationSystem.OnEventRaised += new EventHandler(CustomNotificationSystemHandler)
       _a = new A();

       B b1 = new B(_a);
       B b2 = new B(_a);
       C c1 = new C(_a);
       C c2 = new C(_a);
    }

    void CustomNotificationSystemHandler(args)
    {

        //_a.Dispose(); ADDED BY **EDIT 2***
        a.Dispose();

        _a = new A();
        /*
        b1,b2,c1,c2 will continue to exists as is, and I know they will now subscribed
        to previous instance of _a, and it's OK by me, BUT in that example, now, nobody
        references the previous instance of _a (b not holds reference to _a) and by my
        theory, previous instance of _a, now may be collected...or I'm missing
        something???
        */
    }

}  

class A : IDisposable
        {
           public event EventHandler EventHappened;
        }

        class B
        {          
           public B(A a) //Class B does not stores reference to a internally.
           {
              a.EventHappened += new EventHandler(this.HandleEventB);
           }

           public void HandleEventB(object sender, EventArgs args)
           {
           }
        }

        class C
        {          
           public C(A a) //Class B not stores reference to a internally.
           {
              a.EventHappened += new EventHandler(this.HandleEventC);
           }

           public void HandleEventC(object sender, EventArgs args)
           {
           }
        }

EDIT 2: OK, now it's clear, when subscriber subscribes to a publishers events, it's NOT creates a reference to the publisher in subscriber. Only the reference from publisher to subscriber created (through EventHandler)...in this case when publisher collected by GC before the subscriber (subscribers lifetime is greater then publishers), there's no problem.

BUT...as I know, it's not guaranteed when GC will collect the publisher so in theory, even if subscribers lifetime is greater then publishers, it can happen that subscriber is legal for collection, but publisher is still not collected (I don't know if within closest GC cycle, GC will be smart enough to collect publisher first and then subscriber.

Anyway, in such case, since my subscriber do not have direct reference to publisher and can't unsubscribe the event, I would like to make publisher to implement IDisposable, in order to dispose it before delete all references to him (see in CustomNotificationSystemHandler in my example).

AND AGAIN What I should write in publishers dispose method in order to clear all references to subscribers? should it be EventHappened -= null; or EventHappened = null; or there's no way to do it in such way, and I need to make something like below ???

public event EventHandler EventHappened
   {
      add 
      {
         eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] + value;
      }
      remove
      {
         eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] - value; 
      }
   }

解决方案

I have added My comments in your sample code.

class A : IDisposable
{
   public event EventHandler EventHappened
   {
      add 
      {
         eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] + value;
      }
      remove
      {
         eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] - value; 
      }
   }

   public void Dispose()
   {
      //Amit: If you have only one event 'EventHappened', 
      //you can clear up the subscribers as follows

      eventTable["EventHappened"] = null;

      //Amit: EventHappened = null will not work here as it is 
      //just a syntactical sugar to clear the compiler generated backing delegate.
      //Since you have added 'add' and 'remove' there is no compiler generated 
      //delegate to clear
      //
      //Above was just to explain the concept.
      //If eventTable is a dictionary of EventHandlers
      //You can simply call 'clear' on it.
      //This will work even if there are more events like EventHappened          
   }
}

class B
{          
   public B(A a)
   {
      a.EventHappened += new EventHandler(this.HandleEventB);

      //You are absolutely right here.
      //class B does not store any reference to A
      //Subscribing an event does not add any reference to publisher
      //Here all you are doing is calling 'Add' method of 'EventHappened'
      //passing it a delegate which holds a reference to B.
      //Hence there is a path from A to B but not reverse.
   }

   public void HandleEventB(object sender, EventArgs args)
   {
   }
}

class C
{          
   public C(A a)
   {
      a.EventHappened += new EventHandler(this.HandleEventC);
   }

   public void HandleEventC(object sender, EventArgs args)
   {
   }
}

class Service
{       
    A _a;

    void Start()
    {
       CustomNotificationSystem.OnEventRaised += new EventHandler(CustomNotificationSystemHandler)

       _a = new A();

       //Amit:You are right all these do not store any reference to _a
       B b1 = new B(_a);
       B b2 = new B(_a);
       C c1 = new C(_a);
       C c2 = new C(_a);
    }

    void CustomNotificationSystemHandler(args)
    {

        //Amit: You decide that _a has lived its life and must be disposed.
        //Here I assume you want to dispose so that it stops firing its events
        //More on this later
        _a.Dispose();

        //Amit: Now _a points to a brand new A and hence previous instance 
        //is eligible for collection since there are no active references to 
        //previous _a now
        _a = new A();
    }    
}

b1,b2,c1,c2 will continue to exists as is, and I know they will now subscribed to previous instance of _a, and it's OK by me, BUT in that example, now, nobody references the previous instance of _a (b not holds reference to _a) and by my theory, previous instance of _a, now may be collected...or I'm missing something???

As explained through my comments in the above code, you are not missing anything here :)

BUT...as I know, it's not guaranteed when GC will collect the publisher so in theory, even if subscribers lifetime is greater then publishers, it can happen that subscriber is legal for collection, but publisher is still not collected (I don't know if within closest GC cycle, GC will be smart enough to collect publisher first and then subscriber.

Since publisher references subscriber, it can never happen that the subscriber becomes eligible for collection before the publisher but reverse can be true. If publisher gets collected before subscriber then, as you said, there is no problem. If the subscriber belongs to a lower GC generation than publisher then since publisher holds a reference to subscriber, GC will treat the subscriber as reachable and will not collect it. If both belong to same generation, they will be collected together.

since my subscriber do not have direct reference to publisher and can't unsubscribe the event, I would like to make publisher to implement IDisposable

Contrary to what some have suggested, I would recommend implementing dispose if at any point you are deterministically sure that the object is no longer required. Simply updating an object reference may not always lead to an object stop publishing events.

Consider the following code:

class MainClass
{
    public static Publisher Publisher;

    static void Main()
    {
        Publisher = new Publisher();

        Thread eventThread = new Thread(DoWork);
        eventThread.Start();

        Publisher.StartPublishing(); //Keep on firing events
    }

    static void DoWork()
    {
        var subscriber = new Subscriber();
        subscriber = null; 
        //Subscriber is referenced by publisher's SomeEvent only
        Thread.Sleep(200);
        //We have waited enough, we don't require the Publisher now
        Publisher = null;
        GC.Collect();
        //Even after GC.Collect, publisher is not collected even when we have set Publisher to null
        //This is because 'StartPublishing' method is under execution at this point of time
        //which means it is implicitly reachable from Main Thread's stack (through 'this' pointer)
        //This also means that subscriber remain alive
        //Even when we intended the Publisher to stop publishing, it will keep firing events due to somewhat 'hidden' reference to it from Main Thread!!!!
    }
}

internal class Publisher
{
    public void StartPublishing()
    {
        Thread.Sleep(100);
        InvokeSomeEvent(null);
        Thread.Sleep(100);
        InvokeSomeEvent(null);
        Thread.Sleep(100);
        InvokeSomeEvent(null);
        Thread.Sleep(100);
        InvokeSomeEvent(null);
    }

    public event EventHandler SomeEvent;

    public void InvokeSomeEvent(object e)
    {
        EventHandler handler = SomeEvent;
        if (handler != null)
        {
            handler(this, null);
        }
    }

    ~Publisher()
    {
        Console.WriteLine("I am never Printed");
    }
}

internal class Subscriber
{
    public Subscriber()
    {
        if(MainClass.Publisher != null)
        {
            MainClass.Publisher.SomeEvent += PublisherSomeEvent;
        }
    }

    void PublisherSomeEvent(object sender, EventArgs e)
    {
        if (MainClass.Publisher == null)
        {
            //How can null fire an event!!! Raise Exception
            throw new Exception("Booooooooommmm");
            //But notice 'sender' is not null
        }
    }
}

If you run the above code, more often than not you will receive the 'Booooooooommmm'. Hence idea is that event publisher must stop firing events when we are sure that its life is up.

This can be done through Dispose method.

There are two ways to achieve this:

Set a flag 'IsDisposed' and check it before firing any event. Clear up the event subscribers list (as suggested in my comments in your code).

Benefit of 2 is that you release any reference to the subscribers, thereby enabling there collection (as I explained earlier even if the publisher is garbage but belongs to higher generation then it may still prolong collection of lower generation subscribers).

Though, admittedly, it will be quite rare that you experience the demonstrated behavior due to 'hidden' reachability of the publisher but as you can see benefits of 2 are clear and are valid for all event publishers especially long living ones (Singletons anybody!!!). This itself makes it worth to implement Dispose and go with 2.