调度程序。优先级问题

时间:2019-02-11 15:14:14

标签: c# .net wpf dispatcher

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

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

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

大多数情况下有效:每次迭代后都会重新渲染UI,我可以单击按钮取消操作,因此输入也可以正常工作。问题是:如果没有输入,也没有要重新呈现的内容,则该方法将陷入“收益”中。大概该线程被阻塞,等待输入或渲染任务。

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

我该如何解决?

3 个答案:

答案 0 :(得分:1)

尝试使用await Task.Delay(10);await Dispatcher.BeginInvoke(new Action(() => { }), System.Windows.Threading.DispatcherPriority.Input);代替Dispatcher.Yield

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

答案 1 :(得分:0)

如果我将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。两种方法都可以正常工作。

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

答案 2 :(得分:0)

或者不管您在哪个线程上运行,都怪异的怪兽起作用:

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

Dispatcher.Yield()在UI线程上工作正常。但这是一种在Dispatcher.CurrentDispatcher上运行的静态方法,并且没有等效的非静态成员。