Dispatcher.Yield 优先问题
Dispatcher.Yield priority issue
我想循环更新 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.Yield
。
这应该让 UI 线程有机会在循环执行时呈现。
If I increase the DispatcherPriority to Render, the method does not get stuck anymore but then the UI isn't updated and input isn't handled anymore.
其实问题是你把优先级改错了方向。将优先级设置为 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
。无论哪种方式都可以。
接受的答案中建议的其他想法也可以工作,但与简单地屈服于正确请求的继续优先级相比,它们有点笨拙。
或者无论你运行在哪个线程都有效的怪异怪物:
await Task.Run(() =>
{
Action action = () => { };
MainWindow.Dispatcher.Invoke(action,
System.Windows.Threading.DispatcherPriority.Background);
});
Dispatcher.Yield()
在 UI 线程上工作正常。但它是一个对 Dispatcher.CurrentDispatcher
进行操作的静态方法,并且没有等效的非静态成员。
我想循环更新 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.Yield
。
这应该让 UI 线程有机会在循环执行时呈现。
If I increase the DispatcherPriority to Render, the method does not get stuck anymore but then the UI isn't updated and input isn't handled anymore.
其实问题是你把优先级改错了方向。将优先级设置为 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
。无论哪种方式都可以。
接受的答案中建议的其他想法也可以工作,但与简单地屈服于正确请求的继续优先级相比,它们有点笨拙。
或者无论你运行在哪个线程都有效的怪异怪物:
await Task.Run(() =>
{
Action action = () => { };
MainWindow.Dispatcher.Invoke(action,
System.Windows.Threading.DispatcherPriority.Background);
});
Dispatcher.Yield()
在 UI 线程上工作正常。但它是一个对 Dispatcher.CurrentDispatcher
进行操作的静态方法,并且没有等效的非静态成员。