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

C# 本地作用域计时器的预期行为是什么?,c#,.net,timer,garbage-collection,C#,.net,Timer,Garbage Collection,具体地说,如果在本地作用域中创建计时器实例,然后从该作用域返回: 1) 计时器还会执行吗 2) 垃圾什么时候收集 我提供以下两种情况: Timer timer = new Timer(new TimerCallback((state) => { doSomething(); })); timer.Change((int)TimeSpan.FromSeconds(30), (int)TimeSpan.FromSeconds(30)); return; 及 如果您谈论的是System.Thr

具体地说,如果在本地作用域中创建计时器实例,然后从该作用域返回:

1) 计时器还会执行吗

2) 垃圾什么时候收集

我提供以下两种情况:

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


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


另一方面,如果您想要一个一次性的Fire-and-forget计时器,您可以通过创建一个引用计时器的state对象来实现,这样它就可以在计时器事件触发时自行处理。我有一个
TimerService
类,它有一个
whenepersed(TimeSpan,Action)
方法,该方法使用此模式,创建超时非常方便,而不必将计时器实例作为包含类中的字段进行管理。

计时器可能会执行,也可能不会执行,取决于垃圾收集是否在时间执行之前运行。这就是为什么最好将计时器的引用保存在堆栈以外的其他位置


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

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
仍然引用了
某物,但是没有任何东西引用了Timer,所以它不见了。

我没有意识到GC如此懒惰。我认为GC.Collect()强制进行最积极的收集。通常它只收集访问成本不高的内容。@JonB:我认为GC的一个理念是,如果不需要,它不应该做任何事情或做很多事情。@Michael Burr:使用read键,但它必须进入某种空闲或睡眠状态,您可能认为这是一个GC运行的好地方
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);
    }
}