c# - Timer(callback,...) 回调函数是否添加到堆栈中?垃圾收集Q与此

标签 c# timer garbage-collection callstack

我有一个功能,需要每天午夜归档 90 天前的电子邮件。我创建了一个类来处理这个问题,下面是示例:

    public void processArchives()
    {
        initializeTimer();
    }

    private void initializeTimer()
    {
        var now = DateTime.Now;
        var tomorrow = now.AddDays(1);
        var durationUntilMidnight = tomorrow.Date - now;

        var t = new Timer(o => { attemptArchivalProcess(); }, null, TimeSpan.Zero, durationUntilMidnight);
    }

    private void attemptArchivalProcess()
    {
        //perform archival
        initializeTimer(); //re-start timer to process tomorrow
    }

问题是,重复调用initializeTimer会导致堆栈溢出(重复函数调用)还是会“永远”正常运行?

我是否应该能够将 processArchives() 作为新线程调用,并保持线程打开,或者在 init 调用之后是否需要某种循环,例如:

    while(!Program.Closing){ sleep(...); }

防止它被垃圾收集?

最佳答案

我认为您已经接近潜在的解决方案。

计时器

回答您的第一个问题:正如您已经得出的结论,计时器将在其委托(delegate)上流逝。委托(delegate)将在单独的线程上执行,每个计时器过去将获得一个全新的自己的堆栈来执行。因此,无休止的计时器事件永远不会触发 StackOverflowException

等到永远?

尝试回答你的第二个问题:你不必必须编写一个无限循环来保持应用程序的运行。但是,您可以这样做,这完全取决于您的应用程序需要什么。权衡利弊。

幸运的是,还有更多可能的解决方案(没有对错,权衡它们以满足您的需求)

您可以考虑的解决方案的列表:

Console.ReadLine()

如果您有控制台应用程序,则只需等待用户输入即可。主线程将永远等待,而不消耗处理器能力。

按照Servy的建议,创建计划任务

这样你就不需要做任何事情来编写无限循环。完成后您的应用程序将直接退出。如果您实际将此应用程序部署给用户,可能不是最漂亮的解决方案。

Windows 服务

您还可以寻求更成熟的解决方案并编写一个 Windows 服务(听起来比实际更复杂,编写一个基本的 Windows 服务非常简单)。这样你也不必费心编写永无休止的循环,Windows 服务将根据设计永远运行(当然你决定停止它)

永无止境的 while 循环的替代方案 - WaitHandle

您还可以使用信号机制(例如使用AutoResetEvent),以便您的主线程可以等待,直到设置某个信号。这样您也不必主动等待(=不消耗处理器周期)。

你有很多选择,这一切都取决于你的具体需求,我无法为你做出决定。 你可以。 :)


所有这些话,让我们举一个例子。单元测试代表您的应用程序。计时器是一种不同的类型,即System.Timers.Timer。您可以将该计时器设置为AutoReset,这样您就不必创建新的计时器。

这里是例子,我希望它对你有意义(如果没有,评论,也许我可以澄清)

    private Timer _processTimer;
    private AutoResetEvent _resetSignal;

    [Test]
    public void YourImaginaryMainApp()
    {
        const int interval = 24 * 60 * 60 * 1000; // every day

        _resetSignal = new AutoResetEvent(false);
        _processTimer = new Timer(interval)
            {
                AutoReset = true
            };
        _processTimer.Elapsed += ProcessTimerOnElapsed;

        _resetSignal.WaitOne( /*infinite*/);
    }

关于c# - Timer(callback,...) 回调函数是否添加到堆栈中?垃圾收集Q与此,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14966958/

相关文章:

ruby - 相互引用的类的内存泄漏

c# - CA2227 With Dictionary 解决方法是什么?

c# - 通过 Azure Function 2.x 的 Http 触发器函数的构造函数注入(inject) ILogger

c# - 在 CaSTLe Windsor 中注册同一接口(interface)的多个实现

linux - 在 linux 中对发送到 CPU 的两个信号使用react

Java Guava 缓存: How to clear all cache entries?

c# - WCF 4 - Soap 和 REST 端点

javascript - 使函数仅在计时器运行时可点击

java - 哪个引用终结器(FinalReference)或弱/幻影/软引用对于GC具有更高的优先级

javascript - 我应该将功能 block 封装在匿名 JavaScript 函数中吗?