【问题标题】:Cancel execution and reexecute on method re-entry取消执行并在方法重新进入时重新执行
【发布时间】:2014-04-20 22:39:05
【问题描述】:

我看到有人问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 **
    }
}

这似乎运行良好,我有一个很好的响应式用户界面,但我有一个让我担心的场景:

  1. 用户发出请求
  2. 用户发出一个新的请求,取消了原来的请求
  3. 新请求在原始已取消请求引发异常之前返回,并使用当前所需的数据填充 UI
  4. 抛出异常并进行清理以覆盖新的请求输出

这可能非常罕见,但我认为这是一种可能性,除非我对此的理解是错误的。

有没有办法确保如果通过取消令牌请求取消任务并启动新任务,则取消发生在新任务启动/返回执行之前而不阻塞 UI 线程?

任何阅读以扩大我对此的理解将不胜感激。

【问题讨论】:

  • 为什么整理会覆盖结果?在通话之前清除结果。

标签: c# .net task-parallel-library async-await


【解决方案1】:

有什么方法可以确保如果一个任务被取消 取消令牌请求并启动一个新任务 在新任务开始/返回执行之前发生 阻塞 UI 线程?

任何阅读以扩展我对此的理解都是最重要的 赞赏。

首先,根据要求,一些相关的阅读和问题:

如果我正确理解了您的问题,您主要担心的是同一任务的上一个实例在完成后可能会使用过时的结果更新 UI(或 ViewModel)。 p>

为确保不会发生这种情况,请使用 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
}

【讨论】:

  • 您不想同时处理 OperationCanceledException 吗?
  • @Blam,你当然是对的,我最初误解了你。是的,应该在任何地方使用OperationCanceledException 而不是TaskCancelledException。我在复制 OP 的代码时忽略了这一点。
  • 好的,所以这是吹毛求疵的地方,我会有一个问题(OperationCanceledException Ex)
  • @Blam,不用担心,但是为什么在没有正文的情况下 Excatch (OperationCanceledException Ex) {} 中?
  • 无论哪种方式都没有正文。为什么 Catch 作为一般异常,然后测试 OperationCanceledException?
猜你喜欢
  • 1970-01-01
  • 2012-11-09
  • 1970-01-01
  • 2016-02-16
  • 2014-10-19
  • 2020-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多