代码之家  ›  专栏  ›  技术社区  ›  Sky Sanders

取消System.Threading.Timer是否会停止?

c#
  •  13
  • Sky Sanders  · 技术社区  · 14 年前

    如果我有一个活跃的 System.Threading.Timer 我把它设为空,它停止了吗?

    我知道打电话比较合适 .Dispose() 但我想回答书面的问题。

    public class Foo
    {
       private System.Threading.Timer _timer;
       public Foo()
       {
          // initialize timer
       }
    
       public void KillTimer()
       {
          _timer=null;
       }
    }
    

    更新:

    关于是否将对System.Threading.Timer的单个引用设置为空确实会导致停止,这已经表明

    1. 没有延迟引用,例如事件列表,因为线程计时器接受sinlge回调并且不公开事件。
    2. 那个 如果 GC收集,终结器确实会处理timerbase并停止计时器。

    道钉

    using System;
    using System.Threading;
    
    namespace SO_3597276
    {
        class Program
        {
            private static System.Threading.Timer _timer;
    
            static void Main(string[] args)
            {
                _timer = new Timer((s) => Console.WriteLine("fired"), null, 1000, Timeout.Infinite);
                _timer = null;
                GC.Collect();
                Console.ReadKey();
            }
        }
    }
    

    未调用计时器回调。去除 GC.Collect() 然后调用回调。

    谢谢大家。

    4 回复  |  直到 14 年前
        1
  •  6
  •   Sky Sanders    14 年前

    不一定。将其设置为空,删除对它的任何引用,并依赖垃圾收集器来处理它。

    如果计时器在GC到达它之前关闭,它将触发事件。

        2
  •  11
  •   Logan Capaldo    14 年前

    为什么会这样?

    考虑:

      System.Threading.Timer t = ...;
      System.Threading.Timer q = t;
      q = null; // Should this stop it as well?
    

    设置为空是对变量执行的操作,而不是对对象执行的操作。计时器无法知道您将特定变量设置为空,因此无法在此基础上执行操作。

    编辑:

    要解决编辑问题,即使只有一个引用,也不能保证计时器将停止,因为可能在引用设置为空后GC可能不会运行。这也并非完全不可能,Microsoft.NET实现使用代收集器,静态字段很可能在托儿所集合中存活下来,并提升到较老的一代。如果您的程序有一个相对稳定的内存配置文件,那么可能永远不会有旧一代的集合(并且通过扩展,终结器将在程序结束之前不运行)。

        3
  •  7
  •   Community datashaman    7 年前

    我知道你在问 System.Threading.Timer 但是我想指出一些非常重要的事情。

    目前提供的答案是好的。 Logan SLaks 将任何变量设置为 null 对先前分配变量的对象没有直接影响。 Russell is right 当垃圾收集器 但最终还是要处理计时器 停下来。

    slaks表示在设置计时器引用之后 无效的 ,可能存在延迟引用。在一个简单的例子中 系统.线程.计时器 参考文献,事实并非如此。

    但是 ,例如,如果您有 System.Timers.Timer 然后你处理它 Elapsed 事件,然后将其设置为 无效的 留下一个参考和计时器 继续跑下去。

    因此,请考虑以下代码:

    var t = new System.Timers.Timer(1000.0);
    t.AutoReset = true;
    t.Elapsed += (sender, e) => Console.WriteLine(DateTime.Now);
    
    Console.Write("Press Enter to start the timer.");
    Console.ReadLine();
    t.Start();
    
    Console.Write("Press Enter to set t to null.");
    Console.ReadLine();
    
    // This will not stop the timer. It actually does nothing at all to the timer
    // to which t has been assigned.
    t = null;
    
    Console.Write("Press Enter again to perform a garbage collection.");
    Console.ReadLine();
    
    // This STILL will not stop the timer, as t was not the only reference to it
    // (we created a new one when we added a handler to the Elapsed event).
    GC.Collect();
    
    Console.Write("t is null and garbage has been collected. Press Enter to quit.");
    Console.ReadLine();
    

    在上面的示例中,由于代码保存在对 t 为了处理它 逝去 事件,计时器将永不停止。

    再一次,我意识到这不是你所要求的类;我提出这一点只是为了指出,事实上,你对一个给定对象是否有更多的引用并不总是显而易见的。


    更新 :关于我在上面所说的关于 系统.线程.计时器 对象; 它不是 . 要验证这一点,请考虑对上述代码进行以下修改:

    Console.Write("Press Enter to start the timer.");
    Console.ReadLine();
    
    var t = new System.Threading.Timer(
        state => { Console.WriteLine(DateTime.Now); },
        null,
        0,
        1000
    );
    
    Console.Write("Press Enter to set t to null.");
    Console.ReadLine();
    
    // This will not stop the timer. It actually does nothing at all to the timer
    // to which t has been assigned. HOWEVER, if/when the GC comes around to collect
    // garbage, it will see that said timer has no active references; and so it will
    // collect (and therefore finalize) it.
    t = null;
    
    Console.Write("Press Enter again to perform a garbage collection.");
    Console.ReadLine();
    
    // This WILL cause the timer to stop, as there is code in the type's
    // finalizer to stop it.
    GC.Collect();
    
    Console.Write("t is null and garbage has been collected. Press Enter to quit.");
    Console.ReadLine();
    

    这就是它不起作用的原因 系统计时器计时器 (或任何类型的事件,实际上) :

    当我们像这样定义了事件处理程序时,很容易忽略答案:

    t.Elapsed += (sender, e) => Console.WriteLine(DateTime.Now);
    

    如果我像这样定义我的处理程序呢?

    t.Elapsed += (sender, e) => Console.WriteLine(sender.GetType());
    

    哦,对了!那 sender 没有人关注的争论!

    .NET提供的事件处理基础结构要求 处理 事件维护对对象的引用 饲养 事件。否则,本合同提供的签字人 EventHandler 委托及其所有表亲都将受到侵犯。

    这个故事的寓意是:只要向事件添加处理程序,就创建了对对象的新引用。在这一点之后,允许该对象被垃圾收集的唯一方法是移除处理程序——但是如果将对所述对象的唯一其他引用设置为 无效的 (这是.NET程序可能出现“内存泄漏”的少数例子之一)。

        4
  •  2
  •   SLaks    14 年前

    不,它不会停止。

    将变量设置为 null 不会直接产生任何副作用,如停止计时器。(除非是财产)

    由于计时器有其他引用,GC将不会收集它,并且永远不会停止。