WPF:在ui线程上调用时无法获得异常

时间:2014-03-18 16:27:08

标签: c# wpf invoke dispatcher ui-thread

如果在UI-Thread本身上调用它,WPF Dispatcher.Invoke()的行为如何?

通过阅读官方.net 4.5.1来源,我发现了两个有趣的地方。

第一名:

    public void Invoke(Action callback, DispatcherPriority priority, ...)
    {       
        (... guards...)

        // Fast-Path: if on the same thread, and invoking at Send priority,
        // and the cancellation token is not already canceled, then just
        // call the callback directly.
        if(!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess())
        {
            (... synchronization context switch...)
            callback();
            return;
        }

        // Slow-Path: go through the queue.
        (... ...)
    }

...所以在UI-Thread上调用优先级Send直接调用。 OK!

第二名:

link to official source

DispatcherOperation.Wait(时间跨度)

<-DispatcherOperation.Wait()
<--Dispatcher.InvokeImpl(DispatcherOperation, CancellationToken, TimeSpan)
<---Dispatcher.Invoke(Action, DispatcherPriority, CancellationToken, TimeSpan)
<----Dispatcher.Invoke(Action, DispatcherPriority)

if(_dispatcher.Thread == Thread.CurrentThread)
{
    if(_status == DispatcherOperationStatus.Executing)
    {
        // We are the dispatching thread, and the current operation state is
        // executing, which means that the operation is in the middle of
        // executing (on this thread) and is trying to wait for the execution
        // to complete.  Unfortunately, the thread will now deadlock, so
        // we throw an exception instead.
        throw new InvalidOperationException(SR.Get(SRID.ThreadMayNotWaitOnOperationsAlreadyExecutingOnTheSameThread));
    }

    // We are the dispatching thread for this operation, so
    // we can't block.  We will push a frame instead.
    DispatcherOperationFrame frame = new DispatcherOperationFrame(this, timeout);
    Dispatcher.PushFrame(frame);
}
else...

如果在UI线程上调用了Invoke(...),其优先级高于Send,那么它应该抛出异常。所以我试图获得这个例外,只是为了好玩,但没有成功。这是我用于此目的的代码:

    private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
    {
        msLogger.Info("UIElement_OnMouseDown begin");

        Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
        {
            msLogger.Info("BeginInvoke");
        }));

        Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
        {
            msLogger.Info("Invoke");
        }));

        msLogger.Info("UIElement_OnMouseDown end");
    }

我希望在Invoke-call中出现异常......但是没有异常发生...并且日志行按以下顺序排列:

  1. UIElement_OnMouseDown开始
  2. 的BeginInvoke
  3. 调用
  4. UIElement_OnMouseDown结束
  5. ...所以这意味着Invoke调用刷新了整个队列,直到被调用的动作被处理完毕!这个“DoEvents”实现比MSDN中提出的实现简单得多。与此同时,它可能导致非常奇怪和危险的行为。

    为什么前面的代码示例会执行而不会抛出任何异常?

    提前谢谢!

1 个答案:

答案 0 :(得分:0)

实际上,答案是:字段DispatcherOperation._status当前没有值DispatcherOperationStatus.Executing。当前运行的DispatcherOperation不是我们要调用的那个......