【问题标题】:How to destroy class object with timer property on null or new instance assignment to class instance variable in c#?c# - 如何在C#中为类实例变量分配null或新实例时使用timer属性销毁类对象?
【发布时间】:2019-05-16 07:14:14
【问题描述】:

我有一个具有 System.Timers.Timer 属性的类。计时器将在使用构造函数创建对象时开始。

类定义:

public class Temp
{
    public int ID { get; set; } = 0;
    public System.Timers.Timer Timer { get; set; } = null;

    public Temp(int id)
    {
        ID = id;
        Timer = new System.Timers.Timer(5000);
        Timer.Elapsed += Timer_Elapsed;
        Timer.Enabled = true;
        Timer.Start();
        Console.Write(this.ID + "Class Constructor");
    }

    private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        Console.Write(this.ID + "Timer_Elapsed");
    }

    ~Temp()
    {
        Console.Write(this.ID + "Class Distructor");
    }
}

注意:

我没有在类中的任何地方调用 Timer.Stop() 方法或设置 Timer.Enable = false。这意味着只有当析构函数被调用时才会销毁定时器。我写了一个析构函数来知道这个类什么时候被销毁。

要求:

当我将空值或新实例分配给类实例变量时,我需要停止或销毁计时器。我不想通过将其公开并实现诸如 temp.Dispose()temp.StopTimer() 等方法来处理类外部的计时器,其中计时器将停止,然后将一个新的 Temp 类实例分配给 temp 变量。

类使用代码示例:

static class Program
{
    static void Main()
    {
        Temp temp = new Temp(1);
        temp = null; // Or line not nacessary.
        temp = new Temp(2);
        GC.Collect();
        GC.WaitForFullGCComplete();
        Thread.Sleep(Timeout.Infinite);
    }
}

我目前的理解或假设计时器何时会被销毁:

1.程序退出时:

完美运行。 析构函数被调用了两次而不是一次。 不确定这是否正常?

2。将 null 分配给变量:

我假设当分配空值或分配新实例时,旧实例有资格进行垃圾回收。所以我期待调用析构函数。但它没有被调用在调用析构函数或空赋值不会调用垃圾收集器时是否有时间或内存限制?

3.在c#中我们不能显式调用析构函数。

4.显式调用垃圾回收不起作用。

还有其他方法吗?

【问题讨论】:

  • 您应该实现 IDisposable,而不是使用该析构函数。然后确保在完成对象后调用它。
  • 这篇文章可能对您有所帮助:Juval Lowy oreilly.com/library/view/programming-net-components/0596102070/…的确定性最终确定
  • Implemented IDisposable from this link docs.microsoft.com/en-us/dotnet/standard/garbage-collection/… 所以,因为 Timer 是一种非托管类型,我们需要实现 IDIsposable 并调用像 temp.Dispose() 这样的 Dispose 方法并处理 Timer在 Dispose 方法中调用 Timer.Dispose()!!我必须手动销毁定时器!!。而且仍然没有调用析构函数,对此的任何解释都会有所帮助
  • .Net 无法满足您的要求。分配新值时不能隐式调用析构函数。您正在创建的是内存泄漏,并希望框架代表您处理它。析构函数(它实际上是一个终结器)不会那样做。这是做不到的。你需要使用并调用dispose。
  • 没有调用终结器,因为您在对象内部有一个循环引用。为其分配一个新值会孤立旧值,但框架不会调用终结器,因为仍然存在活动引用。它只在程序退出时调用它(是的,它可能会多次调用它,这就是为什么你有一个弃置标志),因为它在进程关闭时清理所有引用。这是内存泄漏。

标签: c# timer


【解决方案1】:
  1. 将 null 分配给变量:

我假设当一个空值被赋值或新实例被赋值时 分配的旧实例有资格进行垃圾收集。我也是 期待调用析构函数。但它没有被调用。是 调用析构函数或 null 时有任何时间或内存限制 赋值不会调用垃圾收集器?

关闭,但不正确。 当没有对对象的剩余引用时,可能与您将引用设置为 null 时一致(如果没有其他引用,即使是非显而易见的引用,例如订阅对象事件之一的东西),那么它就有资格被垃圾收集。然而,直到它真正被 垃圾收集后,才会调用终结器;不是当它符合条件时。 GC 可能会选择不收集您的对象,即使可以。如果一个对象在上次 GC 运行时不合格,则 GC 会猜测它下次不合格,并将其推送到更高的层。长生命周期的对象通常会被固定,直到完整的第 2 层 GC 运行(如果内存压力不够高,这可能永远不会)。作为一般规则,如果您依赖 GC 在某个确定性时间完成对象,那么您做错了什么。

当我分配空值或新值时,我需要停止或销毁计时器 实例到类实例变量

您需要使用引用计数,而不是自动垃圾回收来满足此要求。为此,请将您的引用放在其他东西的属性中,并使用它来隐式跟踪引用:

public class MyTimerReference {
   private Temp temp = null;
   public Temp Value { 
      get {
          return temp;
      } 
      set {
          var oldRef = temp;
          if (value != null)
              value.AddRef();
          temp = value;
          if (oldRef != null)
              oldRef.ReleaseRef();   
      }
   }
}

public class Temp
{
    public int ID { get; set; } = 0;
    public System.Timers.Timer Timer { get; set; } = null;

    public Temp(int id)
    {
        ID = id;
        Timer = new System.Timers.Timer(5000);
        Timer.Elapsed += Timer_Elapsed;
        Timer.Enabled = true;
        Timer.Start();
        Console.Write(this.ID + "Class Constructor");
    }

    private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        Console.Write(this.ID + "Timer_Elapsed");
    }

    private int refCount = 0;
    void AddRef() {
       refCount++;
    }

    void ReleaseRef() {
       refCount--;
       if (refCount == 0) {
          Timer.Stop();
          Console.Write(this.ID + "stopped");
       }
    }
}

现在您可以使用MyTimerReference 的实例执行以下操作:

myTimerReference.Value = new Temp();  
myTimerReference.Value = null;  //this will cause the old `Temp` instance to cleanup

或者

var ref1 = new MyTimerReference();
var ref2 = new MyTimerReference();

ref1.Value = new Temp(); //timer gets started
ref2.Value = ref1.Value; //nothing happens
ref1.Value = null; //nothing happens
ref2.Value = new Temp(); //first timer stops, second timer starts
ref2.Value = null; //second timer stops

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多