theoretisch theoretisch - 4 months ago 32
C# Question

How to cancel a backgroundworker with multiple methods in c# optimal?

I have a backgroundworker with a few different functions and loops.
Every example I found on the internet is only with one loop respectively one function.
Now I want to know if there exists a short solution to cancel the worker with a button.
I only know two solutions which are not optimal I think.

1: Add an if/else to every function/loop -> long and confusing code

loop
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
do calculation
}
}


2: Kill the backgroundworker thread with abort like here (How to “kill” background worker completely?)

I want that the calculation stops immediately after hitting the cancel button no matter which loop or function are executed at the moment.
Are there any better oppotunities than mine?

Answer

When working with a background worker, really the only choice you have is to pepper your code with checks to see if the operation has been cancelled.

How you implement this is entirely up to you, but I'd say you want to check at the start of every potentially-time-consuming operation, e.g. a database query or file system operation, and definitely at the beginning of every loop.

Now, as you've already noticed, it gets trickier when you have multiple methods involved. In that case, storing your worker in a class-level field is probably appropriate so that you can simply check its cancellation status from every method wherever applicable.

One useful approach would be to utilize your own custom Exception class. You could wrap the code in the top-level method within a try-catch block, and then whenever you find out that the background worker has been cancelled somewhere deeper within the call stack, you could just throw an instance of your exception which would be handled by the catch block.

Here's a simple example I worked up.

class CancellationException : Exception
{
    public object State { get; private set; }

    public CancellationException(object state)
    {
        this.State = state;
    }
}

private void DoSomething()
{
    if (_worker.CancellationPending)
    {
        throw new CancellationException("cancelled from blah blah");
    }
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
        DoSomething();
    }
    catch (CancellationException ce)
    {
        e.Cancel = true;
    }
}

Note that the State property in the exception could be used to determine where in your process the cancellation hit so that you can clean up resources, etc.