0

What is the best solution to quickly cancel long running processes inside background worker? For example, we have such situation:

        private void DoWork(object sender, DoWorkEventArgs e)
        {
            ...

            for (int i = 0; i < items; i++)
            {
                if (_worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }
                else
                {
                    VeryLongRunningProcess();
                }
            }
        }
        
        private void VeryLongRunningProcess()
        {
            var a = Test();
            var b = Test2();
            Thread.Sleep(5000);
            var c = Test3();
        }

In such case, VeryLongRunningProcess() will be not finished on pressing cancel until he finished everything inside his body.

What to do in such cases?

I tried to pass (BackgroundWorker)sender to VeryLongRunningProcess() as param and inside this method check for CancellationPending, but i dont know is this correct way or not

UdoQQ
  • 45
  • 5
  • 2
    You can check _worker.CancellationPending inside VeryLongRunningProcess and cancel in different points. – Victor Apr 28 '22 at 09:46
  • Victor, I also came up with this idea, but is it a good solution? especially if the method will be in another class and pass the worker there? – UdoQQ Apr 28 '22 at 09:49
  • Does the `VeryLongRunningProcess` method accept a [`CancellationToken`](https://learn.microsoft.com/en-us/dotnet/api/system.operationcanceledexception.cancellationtoken)? – Theodor Zoulias Apr 28 '22 at 09:52
  • You can't stop something that you can't control. It's that simple. If `VeryLongRunningProcess` should be able to be cancelled before completing then it should provide some means for that to happen. Otherwise, you've just got to wait for it to complete. You could abort a thread that an arbitrary method was executing on but that is very much frowned upon because you have no idea what state the system is in at the time. That's why the `BackgroundWorker` provides a mechanism to perform a clean cancellation from the inside and that's why everything else that needs it should do so too. – John Apr 28 '22 at 09:53
  • @John on the .NET Core platform and later (.NET 6) you can't abort threads. The `Thread.Abort` method throws invariably a `PlatformNotSupportedException`. – Theodor Zoulias Apr 28 '22 at 09:55
  • @Theodor Zoulias, no, it doesn't. Thats the problem – UdoQQ Apr 28 '22 at 09:55
  • So I guess the question is "how can I stop a method in a non-cooperative manner". What platform are you targeting? .NET Framework? .NET 6? – Theodor Zoulias Apr 28 '22 at 09:57
  • @TheodorZoulias, I was talking more in a general sense. I know that `Thread.Abort` has been marked obsolete for some time so I'm surprised it even exists in the latest versions of .NET Core. – John Apr 28 '22 at 10:00
  • @TheodorZoulias Yes, you right. .NET Framework 4 – UdoQQ Apr 28 '22 at 10:01
  • Related questions: [How to "kill" background worker completely?](https://stackoverflow.com/questions/800767/how-to-kill-background-worker-completely) / [What's wrong with using Thread.Abort()](https://stackoverflow.com/questions/1559255/whats-wrong-with-using-thread-abort) – Theodor Zoulias Apr 28 '22 at 10:07
  • If you want avoid use the Worker in your method, use an Action as a parameter: Action cancelOperation. And invoke: VeryLongRunningProcess(() => _worker.Cancel()); In your method, simply invoke cancelOperation() and return to cancel the worker at any moment. – Victor Apr 28 '22 at 10:25
  • This really depends on what exactly `VeryLongRunningProcess()` is doing. Is it calling async methods of the dotnet framework? Most (if not all) of them have overloads that accept a [`CancellationToken`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource?view=net-6.0) which is intended for this purpose. – Good Night Nerd Pride Apr 28 '22 at 10:49

2 Answers2

3

If the problem is isolated your VeryLongRunningProcess from classes like the worker, you can use a Func as a parameter and leave outside your method the worker access

private void VeryLongRunningProcess(Func<bool> isCancelled)
{
    var a = Test();

    if (isCancelled())
    {
        return;
    }

    var b = Test2();

    if (isCancelled())
    {
        return;
    }

    Thread.Sleep(5000);
    var c = Test3();
}

Inside your method, you may check if you must cancel the operation as many times you need. And you can use the Func as a parameter in other methods like Test1, Test2... if any of them takes long time to finish.

Then, you invoke your method in this form:

VeryLongRunningProcess(() => _worker.CancellationPending);

As other people comment, maybe interesting use async/await.

UPDATE

Another way to do if you want choose the use or not of the cancellation:

private void VeryLongRunningProcess(Func<bool> isCancelled = null)
{
    var a = Test();

    // Or: isCancelled != null && isCancelled()
    if (isCancelled?.Invoke() ?? false)
    {
        return;
    }

    // ...
}
Victor
  • 2,313
  • 2
  • 5
  • 13
  • Why is it better to pass a `Func`, and not pass the `BackgroundWorker` directly? – Theodor Zoulias Apr 28 '22 at 11:36
  • 2
    You can remove external dependencies, run method without worker... – Victor Apr 28 '22 at 12:27
  • OK, fair enough. It would be nicer if you could pass a `CancellationToken` instead of `Func`, but I guess it's not possible. – Theodor Zoulias Apr 28 '22 at 12:36
  • You can use a CancellationToken using ref parameter but then you are forced to pass a token. With Func, you can use VeryLongRunningProcess(() => {}). Or even pass null and check for null inside the method (isCancelled?.Invoke()). There are many ways of doing things. Sometimes, there is no one particular one that is better than another and it's up to the developer to choose. – Victor Apr 28 '22 at 12:43
  • Variation `isCancelled != null && isCancelled()` looks more readable as for me. Thank you for answer! – UdoQQ Apr 28 '22 at 13:04
1

Normally you should create long-running process as "async" method (public async Task or Task DoWork()) for resources destribution purposes. "CancelationToken" enables cooperative cancellation between threads, thread pool work items. Also it is possible to propagate a callback delegate that can be invoked when Cancellation Token cancelled or function is compleete.

Andrii
  • 21
  • 4