我有一些UI代码,其中有一个如下所示的方法:
private async Task UpdateStatusAsync()
{
//Do stuff on UI thread...
var result = await Task.Run(DoBunchOfStuffInBackground);
//Update UI based on result of background processing...
}用户界面的目标是随时更新影响其状态的属性更改时相对复杂的计算状态。这里有几个问题:
我正在寻找的是一个干净的模式,它完成了以下工作:
这似乎是一个相当常见的问题,所以我想我想问一下,如果有人知道一个特别干净的方法来做这件事。
发布于 2013-03-13 22:38:47
你不需要使用计时器就能做到这一点。一般而言:
private async Task UpdateStatusAsync()
{
//Do stuff on UI thread...
set update pending flag
if currently doing background processing
{
return
}
while update pending
{
clear update pending flag
set background processing flag
result = await Task.Run(DoBunchOfStuffInBackground);
//Update UI based on result of background processing...
}
clear background processing flag
}我将不得不思考如何在异步/等待的上下文中准确地完成所有这些工作。我在过去对BackgroundWorker做过类似的事情,所以我知道这是可能的。
防止它丢失更新应该是非常容易的,但是它可能会时不时地做一个不必要的后台处理。但是,如果在很短的时间内发布10次更新,当然可以避免做9次不必要的更新(可能,它只会做第一次和最后一次更新)。
如果需要,可以将UI更新移出循环。取决于您是否介意看到中间更新。
发布于 2013-03-13 22:38:24
最简单的方法是使用CancellationToken取消旧状态更新,使用Task.Delay延迟状态更新:
private CancellationTokenSource cancelCurrentUpdate;
private Task currentUpdate;
private async Task UpdateStatusAsync()
{
//Do stuff on UI thread...
// Cancel any running update
if (cancelCurrentUpdate != null)
{
cancelCurrentUpdate.Cancel();
try { await currentUpdate; } catch (OperationCanceledException) { }
// or "await Task.WhenAny(currentUpdate);" to avoid the try/catch but have less clear code
}
try
{
cancelCurrentUpdate = new CancellationTokenSource();
var token = cancelCurrentUpdate.Token;
currentUpdate = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromMilliseconds(100), token);
DoBunchOfStuffInBackground(token);
}, token);
var result = await currentUpdate;
//Update UI based on result of background processing...
}
catch (OperationCanceledException) { }
}但是,如果您更新得非常快,这种方法将为GC创建(甚至)更多的垃圾,而且这种简单的方法总是会取消旧的状态更新,因此如果事件中没有“中断”,UI可能会落在后面。
这种复杂程度是async开始达到极限的地方。如果您需要更复杂的内容,反应性扩展将是一个更好的选择(比如处理“中断”,这样您至少会经常得到一个UI更新)。Rx特别擅长处理时间问题。
发布于 2013-03-13 22:22:38
既然我似乎走在正确的轨道上,我将提出我的建议。在非常基本的伪代码中,这看起来可能会奏效:
int counter = 0;
if (update received && counter < MAX_ITERATIONS)
{
store info;
reset N_MILLISECOND timer;
}
if (timer expires)
{
counter = 0;
do calculation;
}这将让您跳过太多的电话是太接近对方,而计数器将确保您仍然保持最新的用户界面。
https://stackoverflow.com/questions/15396911
复制相似问题