如果收到新请求,如何取消先前的任务?

Posted

技术标签:

【中文标题】如果收到新请求,如何取消先前的任务?【英文标题】:How to cancel previous Task if new request recieved? 【发布时间】:2015-07-03 10:58:10 【问题描述】:

我将尝试在这里简化我的情况,使其更加简洁明了。因此,我正在开发一个 WinRT 应用程序,其中用户在 TextBox 和其 TextChanged 事件中输入文本 2 秒后我需要发出远程请求以根据用户获取数据文本。

现在用户输入文本并且网络请求已被初始化,但用户立即写入另一个术语。所以,我需要取消第一个 Web 请求并触发新的请求。

考虑以下作为我的代码:

private CancellationTokenSource cts;

public HomePageViewModel()

    cts = new CancellationTokenSource();


private async void SearchPeopleTextChangedHandler(SearchPeopleTextChangedEventArgs e)

    //Cancel previous request before making new one        

    //GetMembers() is using simple HttpClient to PostAsync() and get response
    var members = await _myService.GetMembers(someId, cts.Token);

    //other stuff based on members

我知道CancellationToken 在这里发挥作用,但我就是不知道如何。

【问题讨论】:

【参考方案1】:

你已经差不多了。核心思想是单个CancellationTokenSource只能取消一次,所以每次操作都要创建一个新的。

private CancellationTokenSource cts;

private async void SearchPeopleTextChangedHandler(SearchPeopleTextChangedEventArgs e)

  // If there's a previous request, cancel it.
  if (cts != null)
    cts.Cancel();

  // Create a CTS for this request.
  cts = new CancellationTokenSource();

  try
  
    var members = await _myService.GetMembers(someId, cts.Token);

    //other stuff based on members
  
  catch (OperationCanceledException)
  
    // This happens if this operation was cancelled.
  

【讨论】:

是否需要处理 CancellationTokenSource?对 _myService.GetMembers 的调用是否应该在完成后立即发生,这样我们就不会取消不再需要的先前令牌? @bmadtiger:CancellationTokenSource 应该被取消或处置。由于我们总是取消旧的,因此不需要处理。处理 CancellationTokenSources 可能会很麻烦,因为它会导致一些成员抛出异常。 感谢@stephen-cleary - 我一直在尝试同时执行CancelDispose 并在短时间内收到大量重复调用的异常,因为CancellationTokenSource 已经被处理了.这或许可以解释。【参考方案2】:

我会像这样实现GetMembers 方法:

private async Task<List<Member>> GetMembers(int id, CancellationToken token)
    
        try
        
            token.ThrowIfCancellationRequested();
            HttpResponseMessage response = null;

            using (HttpClient client = new HttpClient())
            
                response = await client.PostAsync(new Uri("http://apiendpoint"), content)
                                       .AsTask(token);
            

            token.ThrowIfCancellationRequested();
            // Parse response and return result
        
        catch (OperationCanceledException ocex)
        
            return null;
        
    

其余的只是调用cts.Cancel() 方法并创建CancellationTokenSource 的新实例,然后每次在处理程序中调用GetMembers。当然,正如@Russell Hickey 提到的,cts 应该是全球性的。 (如果该类有多个实例,并且您总是希望在调用此处理程序时取消 GetMembers 方法,则甚至是静态的。通常我还有一个包装结果的类,并有一个附加属性 IsSuccessful 来区分真正的null 操作失败的结果。

【讨论】:

以上是关于如果收到新请求,如何取消先前的任务?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 RxAlamofire 取消先前的请求?

Java并发编程学习12-任务取消(上)

HttpClient - 任务被取消 - 如何获得确切的错误消息?

Java并发编程学习12-任务取消(上)

Java并发编程学习13-任务取消(下)

java并发基础--- 取消与关闭