自动填充字段的任务取消不会取消所有先前的任务

时间:2015-08-05 11:04:39

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

我有一个搜索方法,可以将搜索建议返回给用户界面。每次用户在搜索框中输入新字符时都会触发此方法。

我添加了一些取消代码来取消之前的搜索请求。这可行,但不是所有时间。

private CancellationTokenSource cancellationTokenSource;

private async Task UserSearch(string searchCriteria)
{
    Debug.WriteLine("Searching for {0}....", searchCriteria);

    try
    {
        var cts = new CancellationTokenSource();
        this.Suggestions = await this.SearchAsync(searchCriteria, cts.Token);
    }
    catch (OperationCanceledException)
    {
        Debug.WriteLine("Search({0}) cancelled", searchCriteria);
    }
}

private async Task<IList<string>> SearchAsync(string searchCriteria, CancellationToken cancelToken)
{
    CancellationTokenSource previousCts = this.cancellationTokenSource;
    CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancelToken);

    this.cancellationTokenSource = linkedCts;

    // if previous task running cancel it
    if (previousCts != null)
    {
        previousCts.Cancel();
    }

    linkedCts.Token.ThrowIfCancellationRequested();

    List<string> results =
        (await this.searchProvider.SearchAsync(searchCriteria, linkedCts.Token)).ToList();

    Debug.WriteLine("Search({0}) returned {1} records", searchCriteria, results.Count);

    linkedCts.Dispose();
    this.cancellationTokenSource = null;

    return results;
}

例如。我得到以下调试消息:

SearchTerm changing to: Di 
Searching for Di....
SearchTerm changing to: Dia
Searching for Dia....
Search(Di) cancelled
SearchTerm changing to: Diap
Searching for Diap....
Search(Diap) returned 323 records
Search(Dia) returned 3230 records

正如您所看到的那样,第一个搜索被取消但第二个搜索没有被删除,并且在最后一次搜索后返回给用户的结果不正确。

如何确保以前的任务始终被取消?

3 个答案:

答案 0 :(得分:1)

我认为你可能会使你的解决方案变得复杂一点。您需要做的就是查看是否存在尚未取消的现有操作并取消它。然后执行新搜索。未经测试,但我认为应该这样做。

private CancellationTokenSource cancellationTokenSource;

private async Task UserSearch(string searchCriteria)
{
    Debug.WriteLine("Searching for {0}....", searchCriteria);

    try
    {       
        if(cancellationTokenSource != null && 
           !cancellationTokenSource.IsCancellationRequested)
        {
            cancellationTokenSource.Cancel();
        }

        cancellationTokenSource = new CancellationTokenSource();

        this.Suggestions = await this.searchProvider.SearchAsync(searchCriteria, linkedCts.Token);

        Debug.WriteLine("Search({0}) returned {1} records", searchCriteria, results.Count);

    }
    catch (OperationCanceledException)
    {
        Debug.WriteLine("Search({0}) cancelled", searchCriteria);
    }
}

答案 1 :(得分:0)

您为时太晚取消了以前的查询,请尽快查询(直接在收到用户输入后,而不是在下一个查询中)。

答案 2 :(得分:0)

我认为问题是对 this.cancellationTokenSource 的多线程访问。如果说线程2太晚取消了线程1( searchProvider.SearchAsync 已完成),则可以在赋值后清除this.cancellationTokenSource(即线程2执行 this.cancellationTokenSource = linkedCts; 和之后的线程1调用 this.cancellationTokenSource = null; )。这将有效地完全禁用线程2的取消

因此,你最好在下一个搜索开始之前取消一个搜索,就像@Ned Stoyanov的建议一样

相关问题