关于创建昂贵的 WPF 对象和多线程

发布于 2024-09-25 07:35:36 字数 296 浏览 6 评论 0原文

多线程编程中的经典建议是在后台线程上执行处理器繁重的工作,并将结果返回到 UI 线程进行次要处理(更新标签等)。如果生成 WPF 元素本身就是昂贵的操作怎么办?

我正在使用一个第三方库,它会生成一些密集的元素,渲染时间可能大约为 0.75 秒 - 1.5 秒。生成一个并不算太糟糕,但是当我需要创建其中 5 个来立即显示时,它会明显锁定 UI(包括进度旋转器)。不幸的是,没有任何其他地方可以创建它们,因为 WPF 是线程仿射的。

我已经尝试过 DispatcherPriority.Background 但还不够。处理这个问题的推荐方法是什么?

The classic advice in multithreading programing is to do processor heavy work on a background thread and return the result to the UI thread for minor processing (update a label, etc). What if generating the WPF element itself is the operation which is expensive?

I'm working with a third party library which generates some intense elements, which can take around to 0.75s - 1.5s to render. Generating one isn't too bad, but when I need to create 5 of them to show at once it noticeably locks the UI (including progress spinners). Unfortunately, there isn't any other place to create them because WPF is thread affine.

I've already tried DispatcherPriority.Background but its not enough. What is the recommended way to deal with this problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

瞄了个咪的 2024-10-02 07:35:36

如果创建的对象派生自 Freezable,那么您实际上可以在与 UI 线程不同的线程上创建它们 - 您只需在工作线程上时对它们调用 Freeze,然后就可以将它们转移过来。但是,这对于不是从 Freezable 派生的项目没有帮助。

您是否尝试过一次创建一个?下面的示例没有做任何有用的工作,但它确实展示了如何通过一点点完成大量工作的基本结构:

int count = 100;
Action slow = null;
slow = delegate
{
    Thread.Sleep(100);
    count -= 1;
    if (count > 0)
    {
        Dispatcher.BeginInvoke(slow, DispatcherPriority.Background);
    }
};
Dispatcher.BeginInvoke(slow, DispatcherPriority.Background);

这里的“工作”是休眠十分之一秒。 (因此,如果您将其替换为花费大约同样长的时间的实际工作,您将得到相同的行为。)这会执行 100 次,因此总共需要 10 秒的“工作”。用户界面始终保持合理的响应能力 - 诸如拖动窗口之类的操作变得不太流畅,但它完全可用。将这两个后台优先级更改为“正常”,应用程序就会锁定。

这里的关键是,我们在完成每一小部分工作并排队下一点之后最终返回 - 我们最终总共调用 Dispatcher.BeginInvoke 100 次而不是一次。这使 UI 有机会定期响应输入。

If the objects being created derived from Freezable, then you can actually create them on a different thread than the UI thread - you just have to call Freeze on them while you're on the worker thread, and then you can transfer them over. However, that doesn't help you for items that don't derive from Freezable.

Have you tried creating them one at a time? The following example doesn't do any useful work but it does show how the basic structure for doing a lot of work in little bits:

int count = 100;
Action slow = null;
slow = delegate
{
    Thread.Sleep(100);
    count -= 1;
    if (count > 0)
    {
        Dispatcher.BeginInvoke(slow, DispatcherPriority.Background);
    }
};
Dispatcher.BeginInvoke(slow, DispatcherPriority.Background);

The 'work' here is to sleep for a tenth of a second. (So if you replace that with real work that takes about as long, you'll get the same behaviour.) This does that 100 times, so that's a total of 10 seconds of 'work'. The UI remains reasonably responsive for the whole time - things like dragging the window around become a bit less smooth, but it's perfectly usable. Change both those Background priorities to Normal, and the application locks up.

The key here is that we end up returning after doing each small bit of work having queued up the next bit - we end up calling Dispatcher.BeginInvoke 100 times in all instead of once. That gives the UI a chance to respond to input on a regular basis.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文