WPF应用程序中用于任务调度的DispatcherTimer和普通定时器Timer的区别

43
请解释"DispatcherTimer"和"a regular Timer"之间的区别,@Kent Boogaart在这个主题中建议在多线程WPF应用程序中使用它们作为任务调度器:

需要多线程策略的建议WPF应用程序

在其中一篇文章的评论中(引用):

- 如果DispatcherTimer所做的全部工作都是启动另一个线程,那么使用DispatcherTimer有什么意义呢? ……这些线程不需要在UI线程上启动。您可以使用常规计时器而完全避免中断UI

"a regular Timer"是什么意思?它们(“DispatcherTimer”和“a regular Timer”)在对UI的影响方面有何不同?

(在阅读此帖子之前,我认为在WPF中使用DispatcherTimer是一种自然的方式。什么情况下这不是真的?)

3个回答

68

DispatcherTimer是常规定时器。它在UI线程上触发Tick事件,你可以在UI上执行任何操作。System.Timers.Timer是异步定时器,它的Elapsed事件在线程池线程上运行。你必须非常小心地编写事件处理程序,不允许触及任何UI组件或数据绑定变量。并且,每当你访问同时也在UI线程上使用的类成员时,都需要使用lock语句。

在链接的答案中,Timer类更加适合,因为OP有意尝试以异步方式运行代码。


请注意,'touching' 包括设置 MVVM 视图模型属性,因此使用 DispatcherTimer 是可以的,但是使用 Timer 甚至都不行。 - Simon_Weaver
@Simon_Weaver如果它是一个基本的字符串/数字/布尔属性,那么在定时器的回调中更改该值将完全有效,即使回调不在UI线程上运行也是如此。虽然您确实可能会遇到ObservableCollection属性之类的问题。 - Steven Rands

36

定时器的Tick事件实际上是在创建定时器的线程上触发的,因此在Tick事件中,如果要访问UI元素,必须通过如下所述的dispatcher.begininvoke进行访问。

RegularTimer_Tick(object sender, EventArgs e)
{
    txtBox1.Text = "count" + i.ToString(); 
    // error can not access
    // txtBox1.Text property outside dispatcher thread...

    // instead you have to write...
    Dispatcher.BeginInvoke( (Action)delegate(){
       txtBox1.Text = "count " + i.ToString();
    });
}

在使用Dispatcher Timer时,您可以按以下方式访问UI元素而无需执行begin invoke或invoke操作...

DispatcherTimer_Tick(object sender, EventArgs e)
{
    txtBox1.Text = "Count " + i.ToString();
    // no error here..
}

DispatcherTimer只是相对于常规计时器提供了方便,使得访问UI对象更加容易。


调度程序计时器使用Invoke还是BeginInvoke? - Martin Ch
根据这里的源代码,DispatcherTimer使用BeginInvoke,原因是调度程序队列具有基于优先级的调用机制。http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherTimer.cs,d2a05f6b09eee858 - Akash Kava

21

如果你需要使用 .NET 4.5 的 await 功能,你也可以使用 async 委托来创建计时器。

        var timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromSeconds(20);
        timer.Tick += new EventHandler(async (object s, EventArgs a) =>
        {
            Message = "Updating...";
            await UpdateSystemStatus(false);  
            Message = "Last updated " + DateTime.Now;              
        });
        timer.Start();

这可能也会引起您的兴趣(我的问题是寻找一个异步友好的DispatcherTimer)https://dev59.com/vGjWa4cB1Zd3GeqPrH0z#12442719 - Simon_Weaver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接