使用CancellationTokenSource

时间:2017-07-29 09:49:17

标签: c# multithreading asynchronous task task-parallel-library

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

我有一个搜索文本框,每当用户输入文本框时,对于每个textchanged事件,我调用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”。

2 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:1)

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

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。