【问题标题】:Run timer in background thread在后台线程中运行计时器
【发布时间】:2011-06-22 20:13:52
【问题描述】:

我正在寻找一种在后台线程中使用 System.Windows.Forms.Timer 的方法。我很难让它在后台线程中启动和停止。这就是我正在做的:

private System.Windows.Forms.Timer chkTimer = new System.Windows.Forms.Timer();

然后在 backgroundworker_dowork 方法中我有这个:

     chkTimer.Interval = 2000;
     chkTimer.Tick += new EventHandler(chkTimer_Tick);
     chkTimer.Start();

在 Tick 方法中,我有与计时器相关的代码,但由于某种原因它不会运行。如果我在 ui 线程中声明上述内容,它可以工作。有人可以帮我在后台线程中启动计时器吗?我不想使用 System.Timers 所以请不要建议

谢谢

【问题讨论】:

  • 你想在一个线程中运行一个线程吗?

标签: c# background-thread


【解决方案1】:
using System.Timers;
using System.Threading.Tasks;
.
.
.
var timer = new Timer(250);
.
.
.
private void Initialize()
{
    timer.Elapsed += (o, s) => Task.Factory.StartNew(() => OnTimerElapsed(o, s));
    timer.Start();
}
.
.
.
private void OnTimerElapsed(object o, ElapsedEventArgs s)
{
    //Do stuff
}

如果需要,您还可以去掉 OnTimerElapsed 参数“o”和“s”。

【讨论】:

  • 非常简单优雅的解决方案。无需与后台工作人员混为一谈,或让您的主要任务在不同的线程上运行。
  • 如果没有将 LongRunning 标志传递给 StartNew(),这 1) 使用线程池线程,因此它可能会开始延迟等待线程可用,并且 1) 线程不会是后台线程。
【解决方案2】:

Forms 的计时器通过将消息发布到表单的句柄 - 消息来工作,然后必须由表单的消息循环处理这些消息。后台线程(通常)没有消息循环,因此 WM_Timer 消息在发布时不会去任何地方(您的回调没有被调用)。

使用 System.Timers 有什么问题?

【讨论】:

  • 有没有办法在后台线程中运行 systyem.timer 而不是自己创建?
  • 没有。系统计时器在默认线程池上运行。如果你有一个线程本地的计时器,你将不得不轮询它或阻塞一个事件,等待它触发,因为在它运行时没有任何东西可以中断线程。这相当于在线程中休眠。你能解释一下你想要完成什么吗?
  • 澄清:来自系统定时器的回调在线程池线程上运行。
  • 我真的不明白上面的。但我想做的是这个。我需要检查一些 txt 文件出现时的文件夹。作为不受我控制的程序限制的一部分,我只需要运行 2 个线程。其中一个必须是后台程序,一个必须是 ui。我需要一个在后台程序中的计时器,它每 x 分钟检查一次文件夹中的文件。我不能有 3 个线程。
  • 然后按照另一张海报的建议,在您的线程中使用 Sleep。
【解决方案3】:

我不会完全使用计时器,它的额外负担。根据我的阅读,您希望拥有两个线程,即 UI 和后台线程。

所以,让后台线程管理间隔而不是计时器。

伪代码:

YourFileChecker checker = new YourFileChecker();
checker.CheckInterval = 60000; //milliseconds, the background thread will manage the interval

System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(checker.Check));
t.Start();

然后让 Check 方法执行以下操作:

 while(!_Stop)
 {
      //Do your work here

      //Wait the specified interval before checking again...
      Thread.Sleep(_CheckInterval);
 }

后台线程现在一直在检查,直到它被告知(发出信号)停止。那么你根本不需要计时器,因为线程正在管理间隔。

【讨论】:

  • 这也会导致应用程序在您的线程休眠时无法终止。问题是如何创建一个不会阻塞应用程序退出的 background 线程。
【解决方案4】:

来自the documentation

Windows 窗体计时器组件是单线程的,精度限制为 55 毫秒。如果您需要更准确的多线程计时器,请使用 System.Timers 命名空间中的 Timer 类。

System.Windows.Forms 命名空间中的大多数组件并非设计为在后台线程上运行。

【讨论】:

    【解决方案5】:

    您应该只使用BackgroundWorker。 启用BackgroundWorker 属性WorkerSupportsCancellation。在DoWork 方法中添加:

    while(!yourBGWorker.CancelationPending)
    {
       //Do some work
       Thread.Sleep(2000);
    }
    

    它做你想做的事 - 在后台线程中做一些工作并等待指定的时间段。也可以在拨打yourBGWorker.CancelAsync();后取消进度

    【讨论】:

    • 但是长时间占用一个池线程是个坏主意。而且真正的程序不会 Sleep()。
    • Sleep期间需要取消worker怎么办?
    • 如果应用需要在睡眠期间退出怎么办?
    • @ChrisBordeman 然后你可以添加非常小的睡眠时间和一个计数器。当计数器过去时 - 做工作......虽然我不建议使用这个(我 9 岁的)答案:)。检查stackoverflow.com/a/14594558/754438 答案 - 让计时器触发后台工作并在需要时取消计时器。
    【解决方案6】:

    System.Windows.Forms.Timer 完全基于 Win32 消息,因此需要消息循环。换句话说,它只能在 UI 线程上运行。另一种选择是 System.Threading.Timer,它将在单独的线程中打勾,但您提到它对您不起作用。据我所知,没有办法创建一个将分派到任意线程的计时器(但是您可以自己进行分派)。从技术上讲,可以在您的线程中启动第二个消息循环并在那里运行 Windows 窗体计时器,但我不推荐它(您需要泵送消息等)。通常有更好的方法来做到这一点。

    【讨论】:

    • 基本上,问题在于,要让定时器附加到任意线程,它需要一种dispatch代码(或invoke i>) 到任意线程。没有通用的方法可以做到这一点。通常,调度是使用消息循环 (Win32/WinForms)、调度程序 (WPF) 或自定义机制完成的。
    【解决方案7】:

    我最近写了一篇可能正是您正在寻找的文章。它在 c# 中演示了一个以指定时间间隔运行并使用后台线程执行指定用户操作的通用轮询组件。您可以在主线程上启动它以在工作线程上运行事件。

    示例用法:

    IPoller poller = new UrlPoller(args[0], TimeSpan.FromSeconds(7));
    IPolling pollingComponent = new Polling.Core.Polling(poller);
    pollingComponent.SubscribeForPollingUpdates(PollingAction);
    pollingComponent.Start();
    

    代码及完整用法:

    http://www.avantprime.com/blog/24/an-example-of-repeating-code-using-a-worker-thread-without-using-timers-c

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-18
      相关资源
      最近更新 更多