【发布时间】:2020-01-01 13:23:24
【问题描述】:
我们的一位客户报告了我们的一个加载项变得无响应的问题。报告的错误包含深入 WPF 内部的堆栈跟踪,我不会因此而分散您的注意力。可以说顶级错误为:
调度程序处理已暂停,但消息仍在处理中。
相同的代码在我的机器上完美运行,据我所知,任何其他机器。代码所做的只是实例化一个 WPF 窗口并调用它的 .ShowDialog() 方法。
查看具有相同错误的问题,我找到了如下答案: Dispatcher throws InvalidOperationException on Messagebox.Show in Textchanged event 要么 WPF : Dispatcher processing has been suspended, but messages are still being processed
在这两种情况下,提供的答案都是使用 Dispatcher.BeginInvoke
问题在于此调用是异步,我需要等待用户响应。所以我改用 Dispatcher.Invoke:
Dispatcher.Invoke(Sub() oAIC.ShowDialog(), System.Windows.Threading.DispatcherPriority.Normal)
这又一次在我的机器上完美运行,但这没有任何意义。原始代码在我的机器上也能完美运行。
所以我的问题是:
这还值得追求吗?或者我使用 .Invoke 而不是 .BeginInvoke 的事实是否等同于我直接使用原始的 .ShowDialog() 所以我在浪费时间?
只要说有问题的客户不是合作型的就够了。其他客户会很高兴我给他们一个试用版并报告回来,但这个人只是想要一个解决方案,不想被其他任何事情打扰。由于任何其他客户从未报告过这件事,我什至找不到另一只“豚鼠”来试穿,所以我剩下的就是在这里问它的选项,希望有足够专业知识的人可以告诉我“是的,这是一个很好的前进方向”或“你在浪费时间”。抱歉,如果这不太适合在这里提问的模型,但我已经走到了尽头......
在看到 Peregrine 的回答后进一步澄清
代码是 Office 插件(在本例中为 Microsoft Word)的一部分。我试图显示的 WPF 对话框从功能区栏中的按钮单击向下弹出几个级别。
我把按钮点击后面的代码放到一个函数中
DoTheButtonCode()
并叫它如下
Await System.Threading.Tasks.Task.Run(AddressOf DoTheButtonCode)
当我尝试这个时,出现了一个错误:
PresentationCore.dll 中出现“System.InvalidOperationException”类型的异常,但未在用户代码中处理 附加信息:调用线程必须是 STA,因为许多 UI 组件都需要这个。
现在这很奇怪。在原始按钮单击事件中,system.threading.thread.currentthread 的公寓状态是 STA 但是当我进入 DoTheButtonCode 并检查 system.threading.thread.currentthread 它的公寓状态是 MTA p>
进一步说明
好的 - 尝试了其他方法:
将 DoTheButtonCode 更改为异步函数:
Private Async Function DoTheButtonCode() As Threading.Tasks.Task(Of Integer)
按钮 Click 事件处理方法现在定义为 Private Async Sub,我在其中调用
Await DoTheButtonCode()
这实际上是可行的,除了出现在 DoTheButtonCode 的函数定义中的这个琐碎的警告:“这个异步方法缺少 'Await' 运算符,因此将同步运行”(等等)
因此,虽然这确实有效,但我怀疑我在这里是在开玩笑。
我想我已经准备好放弃了。我将在我原来的 ShowDialog 调用周围添加一个 try/catch 构造,如果至少引发错误,它只会什么都不做,而不是崩溃:(
感谢您的所有帮助,Peregrine
【问题讨论】:
-
Task.Run将使用默认调度程序,该调度程序将调度到线程池线程,该线程不是 STA。您可以编写一个自定义调度程序,它将调度到 STA 线程上;我把它留给读者作为练习,使用谷歌应该很容易找到示例。对于与 UI 相关的事情,更大的问题是它会将其放在非 UI 线程上,因此如果您想在那里执行与 UI 相关的事情,则需要将其分派回DoTheButtonCode中的 UI 线程。 -
如果你想等待用户响应,你应该可以
AwaitBeginInvoke调用;事实上,根据我的经验,如果你不等待它,你会得到一个编译器错误(对于我想要触发并忘记 BeginInvoke 的实例,我在我的代码中警告禁用 pragma)。 -
谢谢克雷格。我会记住这一点,以防我不得不重新审视我在 OP 结尾处所述的“钝器”方法。现在我只会抓住错误,忽略它,什么也不做,希望这对这个特定用户来说是一个“一次性”问题,如果他们第二次点击按钮就可以了,就像字面意思一样其他人。如果失败了,是的,我会考虑创建一个自定义调度程序,但我希望我不必这样做:)
-
我记错了,你甚至不一定需要编写它,有一个包含 STA 任务调度程序的 Microsoft 并行扩展附加包。
-
感谢 Craig 的额外澄清。