【问题标题】:Dispatcher.Yield priority issueDispatcher.Yield 优先级问题
【发布时间】:2019-02-11 15:14:14
【问题描述】:

我想在我的 WPF 应用程序中循环更新某些 UI 元素的位置。每次迭代后,应重新渲染 UI 以使更改可见。可以使用 CancellationToken 随时停止更新过程。由于取消是由用户执行的,因此 UI 必须保持对输入的响应。为此,我编写了以下方法:

public async Task DoStuff(CancellationToken token)
{
    do
    {
        DoLayoutUpdate();

        await Dispatcher.Yield(DispatcherPriority.Input);
    } while (!token.IsCancellationRequested);
}

这主要是可行的:每次迭代后都会重新呈现 UI,我可以单击按钮取消操作,因此输入也可以正常工作。问题是:如果没有输入并且没有要重新渲染的内容,该方法就会卡在 Yield 中。大概是线程被阻塞等待输入或渲染任务。

如果我将 DispatcherPriority 增加到 Render,则该方法不会再卡住,但 UI 不会更新并且输入不再处理。

我该如何解决这个问题?

【问题讨论】:

  • 你试过await Task.Delay(10);或await Dispatcher.BeginInvoke(new Action(() => { }), System.Windows.Threading.DispatcherPriority.Input);吗?
  • 换句话说,您的问题是如何检查调度程序队列是否为空? Dispatcher 是PriorityQueue,那会相当复杂。看来您正在尝试实现DoEvents(),这是错误技术。而是将您的工作分成合理的任务并调用它们,很难推荐更具体的没有细节的东西。你在做游戏什么的 - 有一个way
  • 为什么在代码中更新布局而不是使用例如animated transforms?
  • @PanagiotisKanavos 我不能使用动画变换,布局很重要,需要手动重新计算。
  • @mm8 这两个似乎都可以正常工作。如果你把它变成答案,我会把它标记为正确的。

标签: c# .net wpf dispatcher


【解决方案1】:

尝试使用await Task.Delay(10); 或await Dispatcher.BeginInvoke(new Action(() => { }), System.Windows.Threading.DispatcherPriority.Input); 而不是Dispatcher.Yield。

这应该让 UI 线程有机会在循环执行时进行渲染。

【讨论】:

  • 你不能等待 Dispatcher.BeginInvoke()。
【解决方案2】:

如果我将 DispatcherPriority 增加到 Render,则该方法不会再卡住,但 UI 不会更新并且输入不再处理。

实际上,问题在于您将优先级更改为错误的方向。将优先级设置为 DispatcherPriority.Background 将允许 WPF 完成其工作,然后最终安排继续以允许该方法在 await 之后恢复执行。

即:

public async Task DoStuff(CancellationToken token)
{
    do
    {
        DoLayoutUpdate();

        await Dispatcher.Yield(DispatcherPriority.Background);
    } while (!token.IsCancellationRequested);
}

使用更高的优先级会导致您的延续被安排得太快,给您的循环所有调度程序时间,优先于 WPF 需要做的所有其他事情。

当然,请注意,在没有参数的情况下调用 Dispatcher.Yield() 也将默认使用 DispatcherPriority.Background。无论哪种方式都可以正常工作。

接受的答案中建议的其他想法也可以,但与简单地使用正确请求的继续优先级让步相比,它们有点笨拙。

【讨论】:

    【解决方案3】:

    或者无论你在哪个线程上运行的奇异怪物:

            await Task.Run(() =>
            {
                Action action = () => { };
                MainWindow.Dispatcher.Invoke(action, 
                    System.Windows.Threading.DispatcherPriority.Background);
            });
    

    Dispatcher.Yield() 在 UI 线程上运行良好。但它是一个在Dispatcher.CurrentDispatcher 上运行的静态方法,并且没有等效的非静态成员。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多