我看到一个类似的问题被问到here,但这似乎不适合我的情景。
我们有一个可以执行请求的UI,如果用户想要再次执行请求(使用不同的查询参数),我们要放弃初始请求,忽略其响应并仅使用最新的请求响应。
目前我有:
private readonly IDataService _dataService;
private readonly MainViewModel _mainViewModel;
private CancellationTokenSource _cancellationTokenSource;
//Constructor omitted for brevity
public async void Execute()
{
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Cancel();
}
_cancellationTokenSource = new CancellationTokenSource();
try
{
string dataItem = await _dataService.GetDataAsync(_mainViewModel.Request, _cancellationTokenSource.Token);
_mainViewModel.Data.Add(dataItem);
}
catch (TaskCanceledException)
{
//Tidy up ** area of concern **
}
}
这看起来运行良好,我有一个很好的响应式用户界面,但我有一个让我担心的场景:
这可能非常罕见,但除非我对此的理解是错误的,否则我认为这是可能的。
有没有办法确保如果通过取消令牌请求取消任务并启动新任务,则在新任务启动之前取消发生/在不阻止UI线程的情况下返回执行?
任何阅读以扩展我对此的理解都将非常感激。
答案 0 :(得分:3)
有没有办法确保如果通过a取消任务 取消令牌请求和新任务启动取消 在新任务启动之前发生/没有执行返回执行 阻止UI线程?
任何扩大我对此的理解的阅读都是最重要的 赞赏。
首先,根据要求提供一些相关的阅读和问题:
如果我正确理解了您的问题,那么您主要担心的是同一任务的之前的实例可能会在完成后用过时的结果更新UI(或ViewModel
)。
要确保不会发生这种情况,请在之前使用ThrowIfCancellationRequested
和相应的令牌来更新UI /模型,无处不在你做吧。然后,如果最新的任务实例在上一个较旧的实例之前完成,则无关紧要。较旧的任务将在之前到达ThrowIfCancellationRequested
点,它可能有可能做任何有害的事情,因此您不必await
旧任务:
public async void Execute()
{
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Cancel();
}
_cancellationTokenSource = new CancellationTokenSource();
var token = _cancellationTokenSource.Token.
try
{
string dataItem = await _dataService.GetDataAsync(
_mainViewModel.Request,
token);
token.ThrowIfCancellationRequested();
_mainViewModel.Data.Add(dataItem);
}
catch (OperationCanceledException)
{
//Tidy up ** area of concern **
}
}
另一个问题是,如果上一个任务因TaskCanceledException
之外的任何其他任务失败,该怎么办。在较新的任务完成后也会发生这种情况。由您决定是否忽略此异常,重新抛出或执行其他操作:
try
{
string dataItem = await _dataService.GetDataAsync(
_mainViewModel.Request,
token);
token.ThrowIfCancellationRequested();
_mainViewModel.Data.Add(dataItem);
}
catch (Exception ex)
{
if (ex is OperationCanceledException)
return
if (!token.IsCancellationRequested)
{
// thrown before the cancellation has been requested,
// report and re-throw
MessageBox.Show(ex.Message);
throw;
}
// otherwise, log and ignore
}