【问题标题】:System.Threading.Timer callback is never calledSystem.Threading.Timer 回调永远不会被调用
【发布时间】:2013-09-23 21:41:28
【问题描述】:

我的 System.Threading.Timer(它有一个回调)永远不会可靠地触发。这是我的编程任务的一部分,我在其中输入计时器应该从文本框中运行的时间量。

定时器是这样声明的:

System.Threading.Timer timer = new System.Threading.Timer(WorkerObject.callback, null, delay, Timeout.Infinite);

延迟是简单的int 描述回调第一次触发的延迟(它应该只触发一次)。

回调方法是这样的:

 public static void callback(Object stateinfo)
 {
     stop = true;
 }

所做的只是将标志设置为 true 以停止循环(由 ThreadPool 上的线程运行,实际上是停止线程)。

循环如下所示:

while (!stop)
{
    currentTextbox.Invoke(new Action(delegate()
    {
        currentTextbox.AppendText((counter++) + Environment.NewLine);
        currentTextbox.Update();
     }));
}

我的问题是stop 变量对于任何超过 5000 毫秒的延迟始终为假。有没有办法“强制”回调始终触发?

【问题讨论】:

  • 这个问题可能不适用(我忘记了 API)...但是你启动计时器了吗?
  • 程序只是打印出一个不断递增的计数器,直到计时器停止。 System.Threading.Timer 不需要启动;它会在延迟后自动启动(0 表示没有延迟,但在我的情况下,我指定了延迟)
  • @Jaxidian: System.Threading.Timer 在你创建它时开始。

标签: c# multithreading timer


【解决方案1】:

您需要保留对计时器的引用。

很可能计时器对象正在被垃圾收集,这将运行其终结器,停止计时器。

因此,只要您需要计时器处于活动状态,就一直保持参考。

【讨论】:

  • +1。这是一个隐蔽的问题。如果您在调试器中运行代码(在 Debug 或 Release 模式下),则不会收集计时器。但是如果你在没有附加调试器的情况下运行它,计时器将被收集。第一次遇到那个时让我抓狂
  • @JimMischel:我完全同意。我难忘的时刻是将委托传递给非托管 API。附加调试器后一切正常,但没有调试器,GC 变得更加激进。我想我们都有自己的故事:)
  • 是的,就是这样! @JimMischel,我遇到了同样的问题,在调试器中,计时器会正常工作,但否则计时器会被垃圾收集。
【解决方案2】:

我建议使用CancellationTokenSource:

static CancellationTokenSource Cancel = new CancellationTokenSource();

public static void Callback(object state)
{
    Cancel.Cancel();
}

和你的循环:

while (!Cancel.IsCancellationRequested)
{
    ...
}

这比使用volatile 更简洁,并且当您将简单的概念证明移动到单独的类时更容易移植。请参阅我的博客Polling for Cancellation,了解更多信息。

【讨论】:

    【解决方案3】:

    运行时抖动可能正在将您的while(!stop) 条件优化为while(true)。 将 stop 变量标记为 volatile。

    private volatile bool stop = false;
    

    【讨论】:

    • 可能会起作用,但如果有其他选择,我不建议volatile
    猜你喜欢
    • 2012-02-18
    • 1970-01-01
    • 2015-07-28
    • 2013-11-23
    • 2018-08-08
    • 1970-01-01
    • 1970-01-01
    • 2020-11-18
    • 1970-01-01
    相关资源
    最近更新 更多