Search code examples
c#taskcancellationcancellationtokensource

Cancel Task in async void


Hi I have a listbox Whenever a user selects an item a request is sent to the web Now I want to cancel the previous operation when the user selected the item and then start the new operation.

I used the following codes to do this, I wanted to know if these codes work well. Or should I try another way?

private CancellationTokenSource ts = new CancellationTokenSource();
private async void Subf2mCore(CancellationToken ct)
{
  HtmlDocument doc = await web.LoadFromWebAsync(url);
   ...
  foreach (var node in doc)
  {
    if (!ct.IsCancellationRequested)
    {
      ....
    }
  }
}

and i run func this way

ts?.Cancel();
ts = new CancellationTokenSource();
Subf2mCore(ts.Token);

Solution

  • Technically, you can put it like this, but look: you fire and forget, let return Task for caller to know if Subf2mCore has been completed, failed or cancelled:

    private async Task Subf2mCore(CancellationToken ct)
    {
      HtmlDocument doc = await web.LoadFromWebAsync(url);
       ...
      foreach (var node in doc)
      {
        // Often we cancel by throwing exception: 
        // it's easy to detect that the task is cancelled by catching this exception
        // ct.ThrowIfCancellationRequested();
    
        // You prefer to cancel manually: 
        // your cancellation can be silent (no exceptions) but it'll be 
        // difficult for caller to detect if task completed or not 
        // (partially completed and cancelled)
        if (!ct.IsCancellationRequested)
        {
          ....
        }
      }
    }
    
    // If we don't want to cancel 
    private async Task Subf2mCore() => Subf2mCore(CancellationToken.None);
    

    Usage: do not forget to Dispose CancellationTokenSource instance:

    using (CancellationTokenSource ts = new CancellationTokenSource()) {
      ...
      await Subf2mCore(ts.Token);
      ...
    }
    

    Edit: if you want to cancel from outside:

    private CancellationTokenSource ts = null;
    
    ...
    
    using (CancellationTokenSource _ts = new CancellationTokenSource()) {
      // previous task (if any) cancellation
      if (null != ts)
        ts.Cancel();
    
      // let cancel from outside
      ts = _ts;
    
      try {
        ...
        await Subf2mCore(_ts.Token);
        ...
      }
      finally {
        // task completed, we can't cancel it any more
        ts = null;
      }
    }