Dispatcher.BeginInvoke从不执行操作?

时间:2015-12-11 06:19:17

标签: c# wpf multithreading timer dispatcher

我的代码从未执行过我的操作:

private static Timer timer = new Timer(TimerCallback, null, 5000, 5000);
private static DispatcherOperation dispatcherOperationPrevious = null;
private static void TimerCallback(object state)
{
    DispatcherOperation dispatcherOperation = 
        Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
    {
        Debug.Print("Pass here at: " + DateTime.Now.ToString("O"));
    }));

    dispatcherOperationPrevious = dispatcherOperation;
}

我没有调试痕迹"通过这里......"。

如果我查看BeginInvoke的结果是dispatcherOperation,我得到(对于当前或以前的dispatcherOperation):

dispatcherOperation.Status = Pending
dispatcherOperation.Task.Status = WaitingForActivation

_dispatcherOperationPrevious .Status = Pending
_dispatcherOperationPrevious .Task.Status = WaitingForActivation

为什么" Debug.Print ......"永远不会被执行?

2 个答案:

答案 0 :(得分:1)

这是因为在另一个线程上调用了TimeCallback。当您在没有Dispatcher的线程上调用Dispatcher.CurrentDispatcher时,它将创建一个新的Dispatcher .CurrentDispatcher。 (Dispatcher绑定到一个线程。)

所以,你正在创建一个新的调度程序,但是这个新的调度程序永远不会Run(),你的Action将在非运行的调度程序上排队。

我想你想在GUI调度程序上调用Action。

每个WPF控件都来自DispatcherObject,并且将包含一个属性Dispatcher,其中包含调度程序所在的位置。使用那个。

因此Dispatcher.Invoke可以解释为静态方法调用,但它实际上是存储在controls属性Dispatcher中的Dispatcher实例的方法调用。

修正:

private static Timer timer = new Timer(TimerCallback, null, 5000, 5000);
private static DispatcherOperation dispatcherOperationPrevious = null;
private static void TimerCallback(object state)
{
    DispatcherOperation dispatcherOperation = 

    // Dispatcher here is using the controls dispatcher, the BeginInvoke is not a static method.. (like you probably assumed..
    // So calling the BeginInvoke of the controls property 'Dispatcher'
    Dispatcher.BeginInvoke(new Action(() =>
    {
        Debug.Print("Pass here at: " + DateTime.Now.ToString("O"));
    }));

    dispatcherOperationPrevious = dispatcherOperation;
}

如果TimeCallback中的Dispatcher属性不可用,例如,如果您在业务层(不良做法)深处的某些类中创建了时间,则可以调用Application.Current.Dispatcher得到'main / gui'调度员。

答案 1 :(得分:1)

正如此post中所述,Dispatcher.CurrentDispatcher将从当前主题获取Dispatcher,在您的情况下,它是Timer的主题。如果要从GUI线程调用调试调用,则需要使用Application.CurrentDispatcher

private static void TimerCallback(object state)
{
    DispatcherOperation dispatcherOperation = 
        Application.CurrentDispatcher.BeginInvoke(new Action(() =>
    {
        Debug.Print("Pass here at: " + DateTime.Now.ToString("O"));
    }));

    dispatcherOperationPrevious = dispatcherOperation;
}

但是,我会使用Timer 推荐,而不是使用标准DispatcherTimerDispatcherTimer旨在用于更新UI(无需显式调用)。

  

如果在WPF应用程序中使用System.Timers.Timer,则值得   注意到System.Timers.Timer在不同的线程上运行   用户界面(UI)线程。为了访问用户的对象   接口(UI)线程,有必要将操作发布到   使用Invoke或用户界面(UI)线程的调度程序   BeginInvoke的。使用DispatcherTimer的原因与   System.Timers.Timer是DispatcherTimer在同一个上运行的   线程作为Dispatcher和DispatcherPriority可以在上面设置   DispatcherTimer。