如果在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!
第二名:
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中出现异常......但是没有异常发生...并且日志行按以下顺序排列:
...所以这意味着Invoke调用刷新了整个队列,直到被调用的动作被处理完毕!这个“DoEvents”实现比MSDN中提出的实现简单得多。与此同时,它可能导致非常奇怪和危险的行为。
为什么前面的代码示例会执行而不会抛出任何异常?
提前谢谢!
答案 0 :(得分:0)
实际上,答案是:字段DispatcherOperation._status当前没有值DispatcherOperationStatus.Executing。当前运行的DispatcherOperation不是我们要调用的那个......