取消执行并在方法重新进入时重新执行

本文关键字:执行 新执行 新进入 方法 取消 | 更新日期: 2023-09-27 17:50:20

我在这里看到过一个类似的问题,但它似乎不太适合我的场景。

我们有一个可以执行请求的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,但我有一个场景,我关心的:

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

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

是否有任何方法可以确保如果一个任务通过取消令牌请求被取消,并且一个新的任务被启动,取消发生在新任务启动/返回执行之前,而不阻塞UI线程?

取消执行并在方法重新进入时重新执行

是否有办法确保如果一个任务被取消取消令牌请求,并启动一个新的任务在新任务启动/返回执行之前发生阻塞UI线程?

任何能扩展我对这个问题的理解的阅读都将是最重要的感激。

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

  • 《异步重入,以及处理它的模式》作者:Lucian Wischik
  • 如何避免重入与异步void事件处理程序?
  • 任务排序和重入
  • 正确取消异步操作并再次启动
  • 同步取消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
}