使用 CancellationTokenSource 取消之前的异步任务

Canceling the previous Async Task using CancellationTokenSource

我需要执行 async 任务取消。我知道 CancellationTokenSource 会帮助我实现这一目标。但是我找不到合适的方法。

我有一个搜索文本框,每当用户在文本框中键入内容时,对于每个文本更改事件,我都会调用 GetStocks 方法,如下所示,

public async Task GetStocks()
{
    var stockings = new List<Services.Models.Admin.SiteStockingLevelsModel>();
    IsBusy = true;
    cts?.Cancel();
    cts = new CancellationTokenSource();
    await Task.Run(() => { CreateStockingCollection(); });
    ValidateMaterials();
    IsBusy = false;
}

CreateStockingCollection方法如下图,

private void CreateStockingCollection()
{
    var stockings = _siteStockingLevelsService.GetSiteInventoryLevels(SiteId);
    CreateStockingLevelCompareCollection(stockings);
    StockingLevels =
        _mapper.Map<TrulyObservableCollection<SiteStockingLevelsModel>>(stockings);            

    ((INotifyPropertyChanged)StockingLevels).PropertyChanged +=
        (x, y) => CompareStockingChanges();
    CompareStockingChanges();
}

我的要求是,
示例 假设用户要键入 "Abc"。当用户输入 "A" 时将调用 GetStocks 方法,用户立即输入 "b" 将再次调用 get stocks 方法,在这种情况下我想取消之前的 GetStocks 用字母 "A".

调用的任务

.Net 中异步编程的最佳实践之一是 Async all the way。看起来你的方法不是基于异步的,它也不接受 CancellationToken。您需要将它添加到您的方法中,否则只有 Task.Run 会尝试取消您的任务,但效果不佳。

此外,仅创建 CancellationTokenSource 是不够的 - 您需要在代码中使用它的 .Token 属性 - 这正是该标记:

await Task.Run(() => { CreateStockingCollection(); }, cts.Token);

取消是合作的,因此您需要将其传递给您自己的代码并让它响应该令牌:

public async Task GetStocks()
{
  var stockings = new List<Services.Models.Admin.SiteStockingLevelsModel>();
  IsBusy = true;
  cts?.Cancel();
  cts = new CancellationTokenSource();
  var token = cts.Token;
  await Task.Run(() => { CreateStockingCollection(token); });
  ValidateMaterials();
  IsBusy = false;
}

private void CreateStockingCollection(CancellationToken token)
{
  var stockings = _siteStockingLevelsService.GetSiteInventoryLevels(SiteId, token);
  CreateStockingLevelCompareCollection(stockings);
  StockingLevels =
      _mapper.Map<TrulyObservableCollection<SiteStockingLevelsModel>>(stockings);            

  ((INotifyPropertyChanged)StockingLevels).PropertyChanged +=
      (x, y) => CompareStockingChanges();
  CompareStockingChanges();
}

在这里,我将它传递给 GetSiteInventoryLevels,这听起来像是这项工作的长 运行 部分。 GetSiteInventoryLevels 现在必须获取 CancellationToken 并将其传递给它正在使用的任何 API。