WPF 画布:Children.Add() 挂在后台线程上?
-
03-07-2019 - |
题
...或者...
“我唤醒了WPF深处的什么邪恶?”
我正在后台线程上创建画布并将其渲染为位图。我已经在生产代码中使用它一年多了,没有出现任何问题。我执行以下操作:
- 创建一个画布对象
- 创建一个新的 NameScope 对象
- 将该 NameScope 分配给 Canvas
- 在画布上画出我想要的任何东西
- 使用 Canvas 的大小调用 canvas.Measure()
- 使用 Canvas 的可用矩形调用 canvas.Arrage()
- 调用canvas.UpdateLayout()
- 渲染画布
在绘制步骤中,我总是调用 canvas.Children.Add() 将 UIElements 放到 Canvas 上。这一直有效。
现在,由于某种无法解释的原因,在我正在处理的应用程序的一种特定情况下,对 canvas.Children.Add() 的调用无限期地挂起,阻塞了我的后台线程。我想不出我在已经工作了一年多的代码和这个特定案例之间所做的任何事情有什么不同。
任何人都可以提出对 canvas.Children.Add() 的调用会像这样挂起的可能原因吗?
编辑: :后台线程是一个 STA 线程(后台线程处理模型已就位,因为我无法在 MTA 线程上使用 WPF 处理图像),因此线程公寓模型不应该是罪魁祸首。
编辑#2: :虽然我理解为什么人们建议我从后台线程尝试 Dispatcher.BeginInvoke(),但我不喜欢这个选项,原因有两个:
- 我希望我的后台线程处理在该线程上同步。我的后台线程有一个队列,其他线程将图像作业提交到该队列,并且我的后台线程在每个作业到达它们时处理它。使用 Dispatcher.BeginInvoke() 会增加另一层复杂性,我宁愿避免这种情况。
- 我从来没有 需要 到目前为止。在我的后台线程上同步执行此后台处理很简单 工作过. 。我试图确定这个奇怪的边缘情况可能有什么不同,导致该代码无法工作。如果我无法让它工作,我最终将在不使用 WPF 的情况下重写此处理代码,我也宁愿避免这样做。
解决方案
您的后台线程使用什么公寓模型?
我相信 WPF 需要在 STA 线程上运行。当您生成后台线程时,尝试将其设置为 STA。
更新:
如果 STA 线程不是问题,那么我会尝试将画布分成块。基本上如果你做了:
调度程序.BeginInvoke(...)
从您的线程中,提供的委托被推送到调度程序队列的后面,从而允许执行其他排队的任务。
更新2:
您还可以尝试使用 .NET 框架参考源调试 Canvas 对象的源代码。您可以通过在“工具”->“选项”下的调试选项中打开“启用.net 框架源代码步进”来启用此功能。
其他提示
尝试在后台线程中调用 Dispatcher.Run() 。