Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# backgroundworker RunworkerCompleted vs async await

Updated with answers: The true way of wait until a number of different tasks to be finished would need async await instead of background worker.

#

I know there are numerous discussion about backgroundworker but I've being searched around and cannot find the answer.

Here is my code example(basic logic, the actual code is much longer), I wonder if there is a way to get around this:

 BackgroundWorker MCIATS1Worker = new BackgroundWorker();
    private AutoResetEvent _MCIATS1WorkerResetEvent = new AutoResetEvent(false);

    public MainWindow()
    {
        InitializeComponent();

        MCIATS1Worker = new BackgroundWorker();
        MCIATS1Worker.DoWork += new DoWorkEventHandler(MCIATS1Worker_DoWork);
        MCIATS1Worker.WorkerReportsProgress = true;
        MCIATS1Worker.WorkerSupportsCancellation = true;
        MCIATS1Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(MCIATS1_RunWorkerCompleted);

        for (int i = 1; i <= 10; i++)
        {
            //some code
            MCIATS1Worker.RunWorkerAsync();
            _MCIATS1WorkerResetEvent.WaitOne();
        }

    }

DoWork and runworkercompleted

void MCIATS1Worker_DoWork(object sender, DoWorkEventArgs e)
    {
        //do something here
    }

    void MCIATS1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("hello world");
        _MCIATS1WorkerResetEvent.Set();
    }

For some reasons, the MCIATS1_RunWorkerCompleted won't be triggered until the loop finished. And apparently the WaitOne is holding the loop.
Here is my question,

why RunWorkerCompleted won't be trigger the RunWorkerCompleted when the worker is actually finished the work?

Thank you.

###UPDATED SOLUTION

This is the right way of doing it.

private async void WhateverFunction()
   {
    await Task.WhenAll(MCIATS1WorkerDoWorkAsync(param),...other tasks);
    }


private Task MCIATS1WorkerDoWorkAsync(bkgWorkParameter param)
    {
        return Task.Run(() =>
        {
            //Do whatever
        });
    }
like image 522
Heisenberg Avatar asked Apr 28 '17 21:04

Heisenberg


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.


2 Answers

It happens because when you use a BackgroundWorker it's RunWorkerCompleted event is posted to the SynchronizationContext of the thread that called RunWorkerAsync.

Because you call RunWorkerAsync on the UI thread the event can't run until the UI thread starts processing new messages in the message loop. However you prevented the UI thread from returning to the message loop by your _MCIATS1WorkerResetEvent.WaitOne(); call.

So what it boils down to is _MCIATS1WorkerResetEvent.Set(); is waiting for MCIATS1_RunWorkerCompleted to fire to stop blocking and MCIATS1_RunWorkerCompleted is waiting for _MCIATS1WorkerResetEvent.Set(); to stop blocking the UI thread so it's message to be processed.

Both things are waiting for the other to complete before itself completes and you have a classic deadlock.

There is no need for a for loop for this problem to happen, this same problem would happen with or without out the loop, in fact the loop never gets to run it's 2nd itteration because it will have deadlocked on the first time through so it does not matter that there is a loop at all.

like image 159
Scott Chamberlain Avatar answered Sep 29 '22 01:09

Scott Chamberlain


Depend on what kind of work your MCIATS1Worker_DoWork method do, you can consider to use async-await approach, which makes code a little bid more cleaner.

private async Task MCIATS1WorkerDoWorkAsync()
{        
    await Task.Delay(1000) // do something asynchronously for 1 second
}

private async void MainWindow_Load(object sender, EventArgs e)
{
     for (int i = 1; i <= 10; i++)
     {
        //some code
        await MCIATS1WorkerDoWorkAsync();
        MessageBox.Show("hello world");
     }       
}

Message box will be shown 10 times every 1 second. await keyword will continue loop only after MCIATS1WorkerDoWorkAsync method has successfully finished.

With async-await your form will remain responsive and if DoWork method do some IO operations, then you will not start another thread (as BackgroundWorker do) and whole execution will happens on one thread.

like image 34
Fabio Avatar answered Sep 29 '22 01:09

Fabio