理解调度队列

20

我认为我需要一些帮助来理解调度队列

当新任务到达时,它会被添加到调度队列的开头,当Dispatcher想要处理一个工作项时,它会从开头被移除。

更一般地说:如果有工作,则以FIFO方式将其存储在队列中,并在没有剩余工作时进行处理。

MSDN文档这里提到了一个循环和一个

Dispatcher在循环中处理工作项队列。该循环被称为帧。

但是在这个上下文中,循环在哪里?对我来说,循环是一种迭代某些内容的方法,在达到末尾时重新开始。

的概念是什么?根据MSDN文档,一个帧是队列中的一堆工作项吗?如果是真的,那么应该如何使用静态方法 Dispatcher.PushFrame() ?

最有趣的问题是是否有任何方法可以获取队列的当前状态,特别是队列中有多少项。

如果在之前调用的方法(因此放入Dispatcher队列中)得到执行,那么它是否会立即从队列中移除,还是会在另一个时间段内持续存在?

我知道,问题这么多 :-)


1
首先问题是:为什么?你为什么要处理Dispatcher?通常,Dispatcher只是WPF进程的“消息泵”。它通过WPF UI将所有的消息/事件/命令处理到应用程序代码中(例如鼠标移动、点击等)。当应用程序的Run方法被调用时,它会调用PushFrame来启动该循环。你不能为同一线程启动另一个循环;而且Dispatcher旨在处理UI工作。 - Peter Ritchie
2
你有没有阅读过关于线程模型的内容? - H.B.
1
这是生产者-消费者问题的标准解决方案:http://en.wikipedia.org/wiki/Producer-consumer_problem。 - Hans Passant
1
当调度程序在开头添加/删除项目时,它被称为LIFO。因此,基本上它是一个堆栈。 - Johannes Egger
1
当新的工作到达时,它会被添加到调度程序队列的开头。...那么这就不是一个队列了。你确定这不是一个打字错误吗? - Kyle Delaney
队列是先进先出的。从 https://learn.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues: "除了WM_PAINT消息、WM_TIMER消息和WM_QUIT消息外,系统始终在消息队列的末尾发布消息。这确保窗口按照适当的先进先出(FIFO)顺序接收其输入消息"。 - Scott Howard
1个回答

27
很少有关于Dispatcher的文档,因此您需要分解一下才能了解其内部工作原理。
调度程序基本上是在应用程序的Message Pump周围执行工作的东西。其中一个问题位于windows message loop之上。
因此,只能有一个应用程序调度程序 - 通过Application.Current.Dispatcher访问的全局调度程序对象。通过访问Dispatcher.CurrentDispatcher可以使用其他调度程序,根据文档,它会

获取当前正在执行的线程的Dispatcher,并在尚未与该线程关联时创建新的Dispatcher。

但是,在这个新调度程序上调用Run将会阻塞。
当您执行Dispatcher.PushFrame时,它会将一个内部执行循环推入到Dispatcher中 - 这是frame的一般想法。任何从DispatcherObject继承的内容,例如DispatcherFrame,其调度程序将设置为当前调度程序。我们可以通过查看其构造函数来验证这一点。
private Dispatcher _dispatcher;

protected DispatcherObject()
{
    this._dispatcher = Dispatcher.CurrentDispatcher;
}

当然,仅有一个简单的事件循环是不够的 - 有时您需要颠覆当前事件循环以强制执行其他工作。这就是为什么您需要一个DispatcherFrame。这实际上构成了事件循环。当您将框架推入调度程序时,会发生以下情况:
while (frame.Continue)
        {
            if (!this.GetMessage(ref msg, IntPtr.Zero, 0, 0))
            {
                break;
            }
            this.TranslateAndDispatchMessage(ref msg);
        }

TranslateAndDispatchMessage中,调度程序中的优先级队列在消息被取出后得到评估。

如果一个操作在调度程序上运行的时间很长,它会暂时停止事件循环,并且由于它不响应信号,应用程序似乎停止响应。

这里是一篇文章,它使用一个框架来强制UI响应,通过允许事件循环短暂运行。

至于访问队列,目前没有办法在调度程序外部了解队列的状态。这是一个内部细节,不公开是合理的。


1
非常感谢您详细的回答(对于延迟我很抱歉)。 - marc wellman
7
因此,每个应用程序只能有一个分派器。这并不正确,分派器实例将绑定在线程上。请参阅 Dispatcher.CurrentDispatcher 的摘要:"获取当前正在执行的线程的分派器,并在尚未与该线程关联时创建一个新的。"你可以将 Dispatcher 想象为消息泵的扩展,该扩展使得能够执行委托而不仅仅处理消息代码。 - Jeroen van Langen
“TranslateAndDispatchMessage” 是用于评估 Dispatcher 中优先级队列的操作,取出并执行其中的操作。不过显然这不是 GetMessage 的功能。 - Jim Balter
@JeroenvanLangen 已更正,请查看。 - Asti
@JimBalter 谢谢,我已经改变了措辞以增加清晰度。 - Asti

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