-1

I have a background long running method which uses cancellation token. On cancel, the thread still keeps processing even after the method called on CancellationTokensource.Token.Register is invoked.

Is there a way to stop processing the thread after register is invoked?

CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();

void HandleBackgroundTasks(object args)
{
   var task = Task.Factory.StartNew(() => BackgroundTask(), 
   _cancellationTokensource.Token);

   task.ContinueWith(t =>
   {
      if (t.IsFaulted)
      {
         //handle faulted
      }
   }
}

void BackgroundTask()
{
   _cancellationTokenSource.Token.Register(() =>
   {
      service.CancelTask();//method to reset database values on cancel
   }
   
   //Some code to check whether to run HandleLongrunningTask method

   service.HandleLongrunningTask(); //long running task
}
user3441903
  • 77
  • 10
  • 3
    Please show your code. Cancellation using a cancellation token is cooperative: unless you handle it nothing it going to happen. – Ian Mercer May 19 '21 at 03:12
  • I have added the code snippet in brief. Please suggest @IanMercer – user3441903 May 19 '21 at 03:29
  • It's a `Task` not a `Thread` and your 'long running task' needs to take the cancellation token and it needs to exit when it's marked cancelled. – Ian Mercer May 19 '21 at 03:45
  • As as side note, the `Task.Factory.StartNew` creates neither a "background method" nor a thread. It creates a `Task`. [Tasks are not threads](https://stackoverflow.com/questions/13429129/task-vs-thread-differences). Also using the `Task.Run` [is preferable](https://devblogs.microsoft.com/pfxteam/task-run-vs-task-factory-startnew/) to using the `Task.Factory.StartNew`. And the `ContinueWith` is a [primitive](https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html) method that has become practically obsolete after the advent of the async/await technology. – Theodor Zoulias May 19 '21 at 03:50
  • I tried to use Task.Factory.StartNew(() => service.HandleLongrunningTask(), _cancellationTokenSource.Token); But it does not abort the task as soon as cancellation is requested. Also, since we are already inside Task can we use the cancellationToken which was already passed rather than creating one again for longrunning task – user3441903 May 19 '21 at 03:55
  • @user3441903 as everyone already explained, a `CancellationToken` doesn't abort a thread or task. Your own code should check whether `IsCancellationRequested` is true and exit. That's what `cooperative` and `graceful` cancellation means. Aborting threads is bad because it leaves all kinds of garbage behind - locked files, open connections, temporary files that weren't deleted. That's why *all* Windows programs, since Windows 95, need to use synchronization constructs like ManualResetEvent to tell worker threads to exit rather than abort – Panagiotis Kanavos May 19 '21 at 09:30
  • @user3441903 file handles etc are owned by the process, not the thread. When you abort a thread any file handles, mutexes, semaphores etc created by the thread will remain until the application terminates. – Panagiotis Kanavos May 19 '21 at 09:32
  • @PanagiotisKanavos checking `IsCancellationRequested` would result in refactoring at many places. Could you please give sample to implement - _"use synchronization constructs like ManualResetEvent to tell worker threads to exit rather than abort"_ – user3441903 May 19 '21 at 10:26
  • Then you have to refactor at many places. Again, CancellationToken doesn't abort. Using ManualResetEvent would require the *same* refactoring. Again, an event doesn't abort. It's only a signal that's set by one thread and read by another – Panagiotis Kanavos May 19 '21 at 10:28
  • Tasks aren't threads, they're a *promise* that something will complete and produce something in the *future*. It makes no sense to abort them. They aren't the actual thread that executes. In many cases, there's no thread at all, eg in IO operations, or when a TaskCompletionSource is used. – Panagiotis Kanavos May 19 '21 at 10:32
  • Besides `service.CancelTask();//method to reset database values on cancel` is extremely suspicious. That has nothing to do with cancellation. This looks like an attempt to use `CancellationToken` as if it was a `TransactionScope` or `Dispose()`, to roll back changes. That's one of the things you *don't* want to abort for any reason. Using a CancellationToken would make this a lot easier - pass the token to `HandleLongrunningTask`, use a `using` block to roll back/reset anything that needs reseting, and in the method just add `if (token.IsCancellationRequested) return;`. – Panagiotis Kanavos May 19 '21 at 10:32
  • By using a `using` block you get rollback even if an exception is thrown, even in cases where a `finally` block wouldn't be called. – Panagiotis Kanavos May 19 '21 at 10:37

1 Answers1

1

CancellationToken does not forcefully cause anything to stop processing.

The code that is doing the processing needs to use the CancellationToken, either by passing it into other functions or calling ThrowIfCancellationRequested (or checking IsCancellationRequested), and letting the resulting OperationCanceledException unwind the stack.

Stephen Jennings
  • 12,494
  • 5
  • 47
  • 66