How to wait for a BackgroundWorker to finish to run another BackgroundWorker (C#)?

1.9k Views Asked by At

First of all, I'm still a beginner, so I'd appreciate it if you could have some patience :)

I've been scratching my head so hard today about this.

The thing is, I want to run three different backgroundworkers. But I would like to wait until one has finished to run the next one, and so on.

Each backgroundworker requires time to finish. Long story short, the thing is, I'm using WaitOne() so whenever the previous backgroundworker tells me it's done, I could start running the new one, but the UI thread is freezing until the three backgroundworkers finish.

I'm seting up a different AutoResetEvent, which is the one taking care of the whole waiting and running thing.

Here's the code:

AutoResetEvent _resetRIEvent = new AutoResetEvent(false);
AutoResetEvent _resetHEvent = new AutoResetEvent(false);
AutoResetEvent _resetSEvent = new AutoResetEvent(false);

await Task.Run(() => bgwTestResInd.RunWorkerAsync());
_resetRIEvent.WaitOne();

await Task.Run(() => bgwTestHipot.RunWorkerAsync());
_resetHEvent.WaitOne();

await Task.Run(() => bgwTestSurge.RunWorkerAsync());
_resetSEvent.WaitOne();

MessageBox.Show("Im done for real.");

Also, is it allowed to do this?

private void bgwTestResInd_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    _resetRIEvent.Set();
}


private void bgwTestHipot_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    _resetHEvent.Set();
}


private void bgwTestSurge_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    _resetSEvent.Set();
}
2

There are 2 best solutions below

1
On

Rather than creating background workers and then using contrived mixes of tasks and synchronization primitives to start the workers at different times, just use the TPL from start to finish to do the whole thing:

await Task.Run(() => TheWorkThatTheFirstBGWWasDoing());
await Task.Run(() => TheWorkThatTheSecondBGWWasDoing());
await Task.Run(() => TheWorkThatTheThirdBGWWasDoing());

Or, if all of the operations are CPU bound work, you can simply use a single call to Task.Run:

await Task.Run(() =>
{
    TheWorkThatTheFirstBGWWasDoing());
    TheWorkThatTheSecondBGWWasDoing());
    TheWorkThatTheThirdBGWWasDoing());
});

Done.

If you really must use a background worker (which there is no real good reason for, but anyway) then all you need to do is call RunWorkerAsync in the completed handler of the BGW that it needs to wait for, or, better yet, just have a single BGW that does the work that the first one is doing, then does the work that the second one needs to do, then does the work that the third one needs to do.

1
On

If you still wanna stick to your plan there here is the code

class Program
{

    static void Main(string[] args)
    {

        var test=new Test();
        test.Run();
        Console.ReadKey();
    }

    private class Test
    {
        AutoResetEvent _resetRIEvent = new AutoResetEvent(false);
        AutoResetEvent _resetHEvent = new AutoResetEvent(false);
        AutoResetEvent _resetSEvent = new AutoResetEvent(false);

        private BackgroundWorker bgwTestResInd = new BackgroundWorker();
        private BackgroundWorker bgwTestHipot=new BackgroundWorker();
        private BackgroundWorker bgwTestSurge=new BackgroundWorker();
        public async void Run()
        {
            bgwTestResInd.DoWork += BgwTestResInd_DoWork;
            bgwTestHipot.DoWork += BgwTestHipot_DoWork;
            bgwTestSurge.DoWork += BgwTestSurge_DoWork;

            await Task.Run(() => bgwTestResInd.RunWorkerAsync());
            _resetRIEvent.WaitOne();


            await Task.Run(() => bgwTestHipot.RunWorkerAsync());
            _resetHEvent.WaitOne();

            await Task.Run(() => bgwTestSurge.RunWorkerAsync());
            _resetSEvent.WaitOne();

            Console.Write("Done");
        }

        private void BgwTestHipot_DoWork(object sender, DoWorkEventArgs e)
        {
            Console.WriteLine("BgwTestHipot done..");
            _resetHEvent.Set();
        }

        private void BgwTestSurge_DoWork(object sender, DoWorkEventArgs e)
        {
            Console.WriteLine("BgwTestSurge done..");
            _resetSEvent.Set();
        }

        private void BgwTestResInd_DoWork(object sender, DoWorkEventArgs e)
        {
            Console.WriteLine("BgwTestResInd done..");
            _resetRIEvent.Set();
        }
    }
}

Just add your code to DoWork methods and they will run in the sequence in which you reset your AutoResetEvents