【问题标题】:Invoke timer from background thread从后台线程调用计时器
【发布时间】:2013-10-01 15:37:06
【问题描述】:

我想做什么。我想 SomeMethod 将被定期调用。因此,我想在后台线程方法主体通过后从后台线程启动计时器。 _timer.Start() 被调用,但 TickHandler 没有;

代码:

using Timer = System.Windows.Forms.Timer;

class TestTimer
    {
        private Timer _timer;
        private Thread _thread;

        public TestTimer()
        {
            // create and initializing timer. but not started!
            _timer = new Timer();
            _timer.Tick += TickHandler;
            _timer.Interval = 60000; // 1 minute

            // create and start new thread
            _thread = new Thread(SomeMethod);
            _thread.Start();
        }

        private void TickHandler(object sender, EventArgs e)
        {
            // stop timer
            _timer.stop();

            //some handling

            // run background thread again
            _thread = new Thread(SomeMethod);
            _thread.Start();
        }   

        private void SomeMethod()
        {
            // some operations

            // start timer!
            TimerStart();
        }

        private void TimerStart()
        {
            _timer.Start();
        } 
    }

通过猴子方法我发现如果像这样添加委托

internal delegate void TimerDelegate();

并替换字符串

TimerStart(); 

Application.Current.Dispatcher.Invoke(new TimerDelegate(TimerStart), null);

一切正常。有人可以解释一下有什么诀窍吗?

【问题讨论】:

    标签: c# multithreading timer


    【解决方案1】:

    你有点搞混了。

    如果您想要一个在后台线程触发的计时器,您不必创建线程来启动它(无论哪个线程调用Start 方法)。只需使用System.Timers.Timer,每个Elapsed 事件都会发生在一个线程池线程上。

    如果您想要一个在 UI 线程触发的计时器,因为看起来您正在使用 WPF,您应该使用 System.Windows.Threading.DispatcherTimer,而不是 Windows您一直在使用的表单计时器。您应该在特定的 UI 线程上创建计时器(即调用new),并且每个Tick 事件都将在该线程上发生。同样,从哪个线程调用 Start 并不重要。

    下面是对代码中发生的情况的解释:您正在非 UI 线程上启动 Windows 窗体计时器。这种计时器需要在该线程上运行消息泵,以便它可以接收消息。因为它是一个非 UI 线程,所以没有消息泵。当您使用Dispatcher.Invoke 方法时,您将计时器的创建编组回应用程序的主 UI 线程,使其工作。但这一切都是多余的。如果您想保持代码不变,只需将计时器替换为DispatcherTimer,然后您就可以删除Invoke 调用。

    或者,如果您使用的是 .NET 4.5,您可以使用 await/async 来简化这一切(请务必从 UI 线程调用 SomeMethod):

    async Task SomeMethod(CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            await Task.Run(() => DoAsyncStuff(), ct);
    
            DoUIStuff();
    
            await Task.Delay(TimeSpan.FromMinutes(1), ct);
        }
    }
    

    【讨论】:

    • 我想要一个在 UI 线程上触发的计时器,但我强制从后台线程启动它。我没有创建后台线程来启动计时器:),我使用后台线程来做一些操作,在这些操作之后我想重新启动计时器。所以,实际上,我需要从 UI 线程启动计时器,但我不知道后台线程何时完成工作。因此我尝试从后台线程启动计时器。无论如何,谢谢,我理解你,几乎......我想我需要阅读更多关于线程的内容。
    • 知道了 :) 尝试阅读任务和异步。它们使线程的使用变得更加简单。
    【解决方案2】:

    MSDN可以给你解释一下:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-24
      相关资源
      最近更新 更多