0

I am trying to patch a problem in an existing GUI, most of which was solved by the code pasted below from this answer -> How to wait for a BackgroundWorker to cancel?

private BackgroundWorker worker = new BackgroundWorker();
private AutoResetEvent _resetEvent = new AutoResetEvent(false);

public Form1()
{
    InitializeComponent();

    worker.DoWork += worker_DoWork;
}

public void Cancel()
{
    worker.CancelAsync();
    _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
    // IS THERE ANY WAY TO TELL IF THE BACKGROUNDWORKER STOPPED DUE TO e.Cancel here???
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    while(!e.Cancel)
    {
        // do something
    }

    _resetEvent.Set(); // signal that worker is done
}

My question is added as a comment to the end of the Cancel function. Is there any way to know at this point why the backgroundworker shut down?

Community
  • 1
  • 1
BigBobby
  • 423
  • 1
  • 3
  • 17

1 Answers1

0

There are lots of options. You probably can check CancellationPending…I don't recall if that gets reset when the DoWork handler returns — I doubt it, but if it does you'd wind up with a race between your wait and the handler returning. (I would've just tested it myself, but your question doesn't include a good Minimal, Complete, and Verifiable code example and I didn't trouble myself to create one.)

Another way is to, instead of AutoResetEvent, use TaskCompletionSource<T>, which supports completion/cancellation semantics:

private BackgroundWorker worker = new BackgroundWorker();
private TaskCompletionSource<object> _tcs;

public Form1()
{
    InitializeComponent();

    worker.DoWork += worker_DoWork;

    // Need to reinitialize when you actually start the worker...you
    // didn't show that code, so I'm putting it here
    _tcs = new TaskCompletionSource<object>();
}

public async Task Cancel()
{
    worker.CancelAsync();
    try
    {
        await _tcs.Task;
        // you'll be here if the task completed normally
    }
    catch (TaskCancelledException)
    {
        // you'll be here if the task was cancelled
    }
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    while(!e.CancellationPending)
    {
        // do something
    }

    if (e.CancellationPending)
    {
        _tcs.SetCanceled();
    }
    else
    {
        // if you actually want to return a value, you can set whatever
        // value you want here. You can also use the correct type T for
        // the TaskCompletionSource<T> declaration.
        _tcs.SetResult(null);
    }
}

You can call Cancel() as Cancel().Wait() if you want, but it's even better if you can use await there, so you can avoid blocking a thread.

Even better than that is to switch from BackgroundWorker to Task and CancellationTokenSource. Then the thing you wait on can be the task itself, instead of a proxy for the task.

Community
  • 1
  • 1
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136