【问题标题】:Timer scoping and proper disposal计时器范围和正确处置
【发布时间】:2018-05-15 01:05:34
【问题描述】:

所以我正在开发一个使用 System.Timers.Timer 对象在任务之间等待的任务调度程序。为了使用这个计时器,我将它放入实例化的静态(扩展)方法中并仅在那里使用它。我的问题是它会在该静态方法结束时得到妥善处理,还是我需要做更多的事情?这就是我正在使用的。

public static void Use<T>(this T o, Action<object> work) => work(o);

private void RunLoop()
{
    while (!Stop)
    {
        try
        {
            //Method 1
            new System.Timers.Timer((task.NextRun - DateTime.Now).TotalMilliseconds).Use(timer => {
                timer.Elapsed += (s, e) => {
                    new Thread(new ThreadStart(RunProc)) {
                        Name = task.Title,
                        IsBackground = true
                    }.Start();
                };
                timer.AutoReset = false;
                timer.Start();
            });

            //Method 2
            using (var timer = new System.Timers.Timer((task.NextRun - DateTime.Now).TotalMilliseconds))
            {
                timer.Elapsed += (s, e) =>
                {
                    new Thread(new ThreadStart(RunProc))
                    {
                        Name = task.Title,
                        IsBackground = true
                    }.Start();
                };
                timer.AutoReset = false;
                timer.Start();
            }

        } catch(SqlException ex)
        { //Omitted for size }
        catch (Exception ex)
        { //Omitted for size }
    }
}

方法 1 会正确处理我的计时器,还是我必须调用 dispose 或处理一个 using 语句才能让 GC 得到它?从我一直在阅读的大多数对象来看,当它们超出使用中的 lambda 调用范围时,它们将被 GC 处理。但显然 Timers 的不同之处在于它们连接到一些非托管资源,这可能导致它们持续存在,即使您认为它们会被清理掉,比如超出范围。

【问题讨论】:

    标签: c#


    【解决方案1】:

    虽然在技术上不是必需的,但应始终正确处置计时器(就像几乎所有从 IDisposable 继承的东西一样)。如果你不处理定时器会发生什么?它们将不断累积,直到垃圾收集检测到它们不再可访问,然后它们将被最终确定(这应该会自动执行与处理它们相同的操作,但效率不高)。

    在您的方法 2 中,计时器可能永远不会触发,因为它将在创建后立即释放(在 Elapsed 委托运行之前)。

    那么,问题就变成了,您应该何时处置计时器?好吧,这取决于它的使用方式。如果您只使用它来执行该小代码块一次,您可以在执行结束时处理它。如果它应该触发不止一次,则您需要让计时器的其他外部所有者在不再需要它时将其处理掉。

    【讨论】:

    • 这一切都存在于 Windows 服务中。它的唯一目的是倒计时运行下一个任务的时间。我担心它在使用方法 2 完成之前被处理掉。方法 1 是我目前正在使用的并且它有效,我只是担心它的实现方式会造成内存泄漏。为每次迭代创建新的并在 lambda 中使用,然后从范围中删除并重新创建。我想我可以制作一个更大范围的计时器,但那时它永远不会被释放,只是重置。不确定这是否更好。
    • 对于一次性计时器,您应该在事件委托中处理它。
    【解决方案2】:

    为了结束,我最终为每个动作创建了一个计时器列表,然后只更新间隔,并根据需要回收它们。然后添加了一个特定的功能,以在停止服务时正确停止和处理所有这些。一旦同时安排了 2 个任务,我上面的设置就会失败,因此无论哪种方式都行不通。使用这种新方法,我不必担心,因为它们要么被无限期回收,要么被专门处理掉。

    【讨论】:

      猜你喜欢
      • 2015-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-21
      • 1970-01-01
      • 2015-08-31
      • 2020-12-20
      相关资源
      最近更新 更多