65.9K
CodeProject 正在变化。 阅读更多。
Home

DispatcherFrame 深入解析

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (8投票s)

2011年1月31日

CPOL

3分钟阅读

viewsIcon

49949

DispatcherFrame 深入解析

引言

你是否曾经好奇过 DispatcherFrame 究竟是什么? 本文将为你解释。

DispatcherFrame 深入解析

我想你们几乎都听说过 DispatcherFrame。 让我们试着理解它是什么以及在哪里可以使用它? MSDN 这样说:“表示 Dispatcher 中的一个执行循环”。 我认为,这不足以理解和使用它。 此外,还有一个非常有用的过程的例子,但也很难理解。 如下所示:

[SecurityPermissionAttribute(SecurityAction.Demand, 
	Flags = SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{   
  DispatcherFrame frame = new DispatcherFrame();   
  Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,    
     new DispatcherOperationCallback(ExitFrame),
  frame);   
  Dispatcher.PushFrame(frame);
}
 
public object ExitFrame(object f)
{
  ((DispatcherFrame)f).Continue = false;
  return null;
} 

这个过程就是 DoEvents。 正如你可以从它的标题中猜到的那样,它会强制执行事件。 所以,让我们理解一下 DispatcherFrame 以及这种方法是如何工作的。

让我们从 Dispatcher 类开始。 我想每个使用 WPF 的人都知道它是什么。 它是一个在 WPF 中处理 UI 线程的类。 它包含一个应该执行的项目的队列。 我希望大家都了解 Dispatcher,我不需要详细描述它。

为了理解 Dispatcher 是如何工作的,我使用 ildasm.exe 实用程序来反汇编 WindowsBase 程序集。 我找到了 Dispatcher 类,并查看了它的 IL。 有一个方法 Run(),它启动了 Dispatcher 的工作。 让我们看看这个方法内部

IL_0000:newobj
instance void System.Windows.Threading.DispatcherFrame ::.ctor()
IL_0005:call
void System.Windows.Threading.Dispatcher::PushFrame
	(class System.Windows.Threading.DispatcherFrame)  

创建了新的 DispatcherFrame,并调用了 PushFrame() 方法。 让我们看看这个方法的实现(我这里只包括重要的行)

IL_0039:ldarg.0
IL_0043:call
  instance bool System.Windows.Threading.Dispatcher::GetMessage(valuetype
  System.Windows.Interop.MSG&, int, int32, int32)
 
IL_004d:
call instance void
  System.Windows.Threading.Dispatcher::TranslateAndDispatchMessage(valuetype
  System.Windows.Interop.MSG&)
 
IL_0053:
  callvirt instance bool System.Windows.Threading.DispatcherFrame::get_Continue()
IL_0058: 
  brtrue.s IL_0039 

用通俗易懂的方式来说,它看起来像这样

While (dispatcherFrame.Continue)
{
  Dispatcher.GetMessage();
  Dispatcher.TranslateAndDispatch();
}

所以,当 dispatcherFrameContinue 属性为 true 时,将执行读取系统消息的循环。

让我们回到 MSDN 的定义:“表示 Dispatcher 中的一个执行循环”。 正是这个 WHILE 语句由 DispatcherFrame 表示。

当应用程序启动时,会调用 Dispatcher.Run()Dispatcher 创建一个默认的 DispatcherFrame,它表示应用程序中的主消息循环。 你可以调用 Dispatcher.PushFrame(DispatcherFrame frame) 来启动上面描述的新循环。 Main 循环将被“暂停”,直到 new DispatcherFrame.Continue = false

现在,让我们回到 WPF 的 DoEvents()

[SecurityPermissionAttribute(SecurityAction.Demand,
Flags = SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
  DispatcherFrame frame = new DispatcherFrame();
  Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, 
     new DispatcherOperationCallback(ExitFrame), frame);
  Dispatcher.PushFrame(frame);
}
 
public object ExitFrame(object f)
{
  ((DispatcherFrame)f).Continue = false;
  return null;
}  

让我们跳过安全属性并继续前进。 假设我们有一组非常庞大的操作需要在 UI 线程中执行,并且在此期间应该更新应用程序的 GUI。 由于我们的操作在 UI 线程中执行,GUI 将不会被更新,因为 Dispatcher 将被这些操作占用。

如何解决这个问题?

DoEvents() 中,创建了 new DispatcherFrame(变量 frame)和一个针对 dispatcher 的“Invoke”,优先级为 Background,这设置了 frame 不应该继续(frame.Continue=false)。 Background 优先级是最低的。

当你调用 DoEvents() 时,Dispatcher 中会有一组需要执行的项目。 此外,这组项目包含一个应该更新我们的 GUI 并应该尽快执行的项目。 当调用 PushFrame() 时,主循环被暂停,一个新的循环开始(如上所述)。 Dispatcher 开始获取系统消息并处理项目队列,这取决于其优先级。 Dispatcher 将处理所有项目,其中一个项目是停止我们新循环的操作。 它具有最低的优先级,并将最后执行。 在操作被调用后,新循环停止,流程返回到主循环和庞大的操作处理。

历史

  • 2011 年 1 月 31 日:初始帖子
© . All rights reserved.