0

I've created a backgroundworker to load data from a file. So far it works as intented but when I try to add a way to cancel the operation it doesnt work however I try. For testing I've broken the logic down to miminum but the result stays unchanged. I also confirmed, that the backgroundworker and the caller are not operating on the same thread.

Initialization of the Worker (inside of the class FileLoader):

private BackgroundWorker Worker = new BackgroundWorker();
private ManualResetEvent WorkerDone = new ManualResetEvent(false);

public FileLoader()
{
   Worker.WorkerSupportsCancellation = true;
   Worker.DoWork += Worker_DoWork;
   Worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
}

public void LoadAsync() => Worker.RunWorkerAsync();

public void CancelLoad()
{
   if (!Worker.IsBusy) return;
   WorkerDone.Reset();
   Worker.CancelAsync();
   WorkerDone.WaitOne();
}

Worker Functions:

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    object data = new object();

    Thread.Sleep(5000);
    if (worker.CancellationPending)
    {
        e.Cancel = true;
        return;
    }            
    e.Result = data;
}

private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled) 
    {
        Console.WriteLine("Cancel requested");
        WorkerDone.Set();
    }
    else LoadCompleted?.Invoke(this, e.Result);
}

If no cancel is requested the worker operates as expected and Worker_RunWorkerCompleted is called. But when a cancel is requested Worker_DoWork is terminated and e.Cancel is set to true but Worker_RunWorkerCompleted is never called in any way and the cancel operation freezes.

According to the documentation of Backgroundworker the RunWorkerCompleted event should always fire when the assigned function ends regardless of an cancel or not.

Mazzelfassel
  • 13
  • 1
  • 4
  • 2
    It is called *deadlock*. The RunWorkerCompleted event handler cannot run, the UI thread is hung on the WaitOne() call. Like any thread it can only do one thing at a time. Never hang the UI thread. You forgot to tell us why you need this code to work so concrete advice is hard to come by. – Hans Passant Mar 04 '20 at 14:53
  • As I said above, the backgroundworker is running on a separate thread from the UI, I especially checked that in the debugger. And the reason why this code should work is that based on the Microsoft documentation the Completed-Event should always fire when the Function ends. – Mazzelfassel Mar 04 '20 at 16:29
  • _"the Completed-Event should always fire when the Function ends"_ -- it does "fire", in that the `BackgroundWorker` tries to raise it. But your code can't ever know about because **one of the other promises the documentation tells you** is that the event will be raised on the UI thread (i.e. the thread owning the `BackgroundWorker` object), and that thread can't do anything **because you've blocked it** waiting on the `WorkerDone.WaitOne();` statement. Just like you've already been told. It is highly unlikely that you need `WorkerDone` at all here. Please explain why you think otherwise. – Peter Duniho Mar 04 '20 at 17:23
  • See marked duplicate. While it's blocking on a call to `Thread.Join()` instead of `WaitOne()`, and it uses an explicit flag variable to signal the background worker to stop rather than the built-in cancellation mechanism, it is essentially identical to your scenario. – Peter Duniho Mar 04 '20 at 17:44

0 Answers0