本地作用域计时器的预期行为是什么

本文关键字:是什么 作用域 计时器 | 更新日期: 2023-09-27 17:57:45

具体来说,如果您在本地作用域中创建一个Timer实例,然后从该作用域返回:

1) 计时器还会执行吗?

2) 垃圾什么时候收集?

我提供这两种场景:

Timer timer = new Timer(new TimerCallback((state) => { doSomething(); }));
timer.Change((int)TimeSpan.FromSeconds(30), (int)TimeSpan.FromSeconds(30));
return;

Timer timer = new Timer(new TimerCallback((state) => { doSomething(); }));
timer.Change((int)TimeSpan.FromSeconds(30), Timeout.Infinite);
return;

本地作用域计时器的预期行为是什么

TimerCallback引用了方法DoSomething(),因此(在您的示例中)引用了this,但没有其他的实时引用,因此应该收集它。。。最终

计时器可能会执行,也可能不会执行,这取决于垃圾收集是否在时间执行之前运行。这就是为什么在堆栈之外的其他地方保留对计时器的引用是一种很好的做法。

请注意,这并不总是有问题的;例如,只要线程仍在运行,就不会对其进行收集。

这里有一个快速测试:

class Program
{
    static void Main(string[] args)
    {
        Something something = new Something();
        Foo(something);
        Console.ReadKey(true);
        GC.Collect();
        Console.ReadKey(true);
    }
    private static void Foo(Something something)
    {
        Timer timer = new Timer(new TimerCallback(something.DoIt),null,0,5);
        return;
    }
}
public class Something
{
    public void DoIt(object state)
    {
        Console.WriteLine("foo{0}", DateTime.Now.Ticks);
    }
}

这基本上就是编译器将其分解为的结果(示例中的Lambda表达式)。当你运行这个程序时,你会注意到,只要你没有按下第一个键,它就会一直把东西放在控制台上。只要你按下一个键,GC就会启动,它就会停止。Timer仍然有对Something的引用,但没有对Timer的引用,所以它不存在了。

如果您谈论的是System.Threading.Timer,它实现了IDisposable,因此您应该维护对它的引用,以便在不再使用它时可以调用Dispose。我不知道您特定问题的答案,但您可以在控制台应用程序中对其进行调查,方法是运行多次迭代并强制GC.Collect()查看计时器是否继续启动。我的猜测是,它最终会被收集并停止激发,除非在内部创建了一些静态根引用。

另一方面,如果您想要一次性的即发即弃计时器,您可以通过创建一个引用timer的状态对象来实现它,这样它就可以在计时器事件触发时自行处置。我有一个TimerService类和一个使用这种模式的WhenElapsed(TimeSpan, Action)方法,它非常方便地创建超时,而不必将Timer实例作为包含类中的字段来管理。