【问题标题】:Can a timer be abandoned safely可以安全地放弃计时器吗
【发布时间】:2012-02-04 15:46:04
【问题描述】:

在不调用 Dispose() 或 Close() 的情况下放弃 .NET 中的计时器是否安全?

static System.Timers.Timer timer = new Timer();

void Main()
{
    timer.Elapsed += LogTimer_Elapsed(object, System.Timers.ElapsedEventArgs);
    timer.Start();
    Thread.Sleep(10000); // Simulate doing something on main thread
}

static void LogTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    DoStuff();
}

有人发现此解决方案有任何问题吗?

static QueueLogger()
{
    LogQueue = new Queue<KeyValuePair<Logger, LogEntry>>(50);
    LogTimer = new Timer();
    LogTimer.Elapsed +=new System.Timers.ElapsedEventHandler(LogTimer_Elapsed);
    AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
}

static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
    LogTimer.Stop();
    LogTimer.Dispose();
    LogTimer_Elapsed(sender, null);  // This is to process any remaining messages in the queue
}

static void LogTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    lock (_locker)
    {
        while (LogQueue.Count > 0)
        {
            var queuedLogger = LogQueue.Dequeue();
            try
            {
                if (e != null) queuedLogger.Value.Message += " From " + sender.ToString();
                queuedLogger.Key.Log(queuedLogger.Value);
            }
            catch (Exception ex)
            {
                OnLoggingError(queuedLogger.Key, "Async Logging error", ex);
            }
        }
    }
}

【问题讨论】:

  • 计时器不是问题。真正的问题是您在 Elapsed 事件中运行的代码在进程终止时是否可以安全中止。或许,说不清。不要做任何事情,比如更新数据库或写入文件。
  • @Hans:实际上写入文件正是我们要做的。这是想法。我想扩展一个日志框架以允许使用计时器和队列进行异步日志记录。但是我不希望日志记录网络的用户不得不费心处理,因为其他记录器类型都不需要这个。

标签: c# .net multithreading timer


【解决方案1】:

高尔福德,

是的,您可以创建新事件,如果您的应用程序即将退出,该事件将触发,例如OnExit 并在应用程序的主线程即将关闭时监听事件。完成后,您可以创建事件逻辑并在 OnExit 事件中执行计时器、业务或清理逻辑。希望这会有所帮助。

问候

【讨论】:

  • 我应该使用的 OnExit 事件处理程序在哪里?
  • 您可以附加或连接到 AppDomain 进程退出事件,如下所示, static void Main(string[] args) { AppDomain.CurrentDomain.ProcessExit += new EventHandler(Application_OnExit); // 做一些工作 } void Application_OnExit(object sender, EventArgs e) { // 做定时器清理等 }
  • 谢谢,我找到了。我用我对任何 cmets/suggestions 的实现更新了我的原始帖子。
【解决方案2】:

如果您没有明确地处理它,它将在垃圾收集器的终结器队列中被清除。尽管这是“安全的”,但您会招致性能损失。最好丢弃你的计时器。

【讨论】:

  • 有没有办法知道应用程序是否正在退出?我的目的是编写一个库,该库将使用计时器定期执行某些操作,但我不希望用户被要求处理和清理库中的任何内容。
  • 那会招致什么惩罚?
  • @galford13x 为什么不希望用户清理一下?这就是 IDisposable 模式的工作方式。任何需要释放系统资源的类都应该通过它的终结器来完成,所以如果有人忘记释放一个对象,你就不会感到厌烦。
  • 我建议你的库有一个 Initialize() 和 Shutdown() 方法,前者会分配和启动你的计时器,后者会停止并处理它。您也可以处理任何其他资源分配和清理。仅供参考 - 这里有一篇关于 3 种不同 .NET 计时器风格的优秀 MSDN 文章:msdn.microsoft.com/en-us/magazine/cc164015.aspx
  • @holtavolt 为什么不简单地使用构造函数和处置模式?这是众所周知的使用模式。
【解决方案3】:

来自http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx

“当不再需要计时器时,使用 Dispose 方法释放计时器持有的资源。”

因此,如果您希望计时器一直运行到程序结束,则无需担心。

【讨论】:

    【解决方案4】:

    如果您不处理 Timer,垃圾收集将清理它。如果计时器在 .exe 中,并且在计时器结束时将退出进程,您真的不必担心。如果您编写了一个将引用计时器的库,您可能希望将其丢弃以释放资源以提高您的库的效率。

    【讨论】:

      【解决方案5】:

      调用 Dispose() 允许在垃圾收集器决定清理托管对象之前清理托管对象使用的非托管资源,在此操作期间非托管资源也会被清理。

      调用 Dispose() 通常是为了优化内存使用,但是,在某些情况下,不调用 Dispose() 实际上会导致您的软件无法正常运行。例如:由于以太网端口的数量是有限的,使用后不释放它们可能会导致系统耗尽网络端口。这通常称为“TCP/IP 端口耗尽”,当您不对使用网络资源的托管对象(例如 WCF 客户端)调用 Dispose() 时,可能会发生这种情况。

      作为一般规则,当您不再需要任何类实现 IDisposable 的对象时,调用 Dispose() 始终是一个聪明的主意。 (或在 using{} 块中使用它)。

      在您提供的示例中,您的计时器变量似乎是静态的,并且绑定到主线程。似乎您正在使用计时器来结束程序。所以在这种特定情况下,这真的无关紧要。

      【讨论】:

      • 提供的代码是为了提供一个简单的例子。不过我明白你的意思。就我而言,我有一个提供日志记录功能的单独程序集。我想通过添加一个异步记录器来扩展程序集。一般来说,大多数记录器不需要特殊的关闭代码,因为它通常包含在记录框架中。我不想将要求添加到这个已经很好的简单框架中。
      猜你喜欢
      • 2018-04-19
      • 1970-01-01
      • 2020-05-06
      • 2017-04-25
      • 2014-01-08
      • 1970-01-01
      • 2017-06-18
      • 2017-04-27
      • 1970-01-01
      相关资源
      最近更新 更多