2

I need to use 4 background workers to move 4 objects on a specific coordinates in parallel i.e. start them together and stop them together.

I wrote a loop to loop 50 times and each time I need to start the workers and after they complete their work as in the Do_Work() method stop it and start them again next iteration, I wrote the following methods to call the workers:

public void Genetic_Algorithm(List<int[,]> population)
        {


            DateTime startT = DateTime.Now.Date;

            double[,] FitnessValue = new double[6, 2]; // for all five chromosome we store two Values the Fitness Value and the Fitness Ratio

            int[] RouletteWheel = new int[6];

            int round = 0;



            for (geneticIteration = 0; geneticIteration < 50; geneticIteration++)
            {



                round = geneticIteration + 1;


                // Calculate the fitness Function and the Fitness Ratio

                FitnessFunction(population); // Fitness Function


            }



            MessageBox.Show("Press Again");


        } 




 public void FitnessFunction(List<int[,]> population)
        {

            extractPath(population, geneticIteration);

            auv0Genetic.RunWorkerAsync(); // start obj # 1
            auv1Genetic.RunWorkerAsync(); // start obj # 2
            auv2Genetic.RunWorkerAsync(); // start obj # 3
            auv3Genetic.RunWorkerAsync(); // start obj # 4


        }

Their are 4 methods Do_Work() for the 4 background workers, the following is one of them:

private void auv0Genetic_DoWork(object sender, DoWorkEventArgs e)
        {

            List<PointF> genetic2DLayerPath1 = new List<PointF>(); //  from chromosome 1

            List<PointF> genetic2DLayerPath2 = new List<PointF>(); //  from chromosome 2

            List<PointF> genetic2DLayerPath3 = new List<PointF>(); //  from chromosome 3

            List<PointF> genetic2DLayerPath4 = new List<PointF>(); //  from chromosome 4

            List<PointF> genetic2DLayerPath5 = new List<PointF>(); //  from chromosome 5

            List<PointF> genetic2DLayerPath6 = new List<PointF>(); //  from chromosome 6

            countNumOfPaths = 0;

            float[] xPoints = new float[1];

            float[] yPoints = new float[1]; 


            foreach (int[,] arr in pathChromosom1)
            {

                Point3D pointIn3D = new Point3D(cellsCenters[0, arr[0, 0]], cellsCenters[1, arr[1, 0]], 700);

                PointF pointIn2D = Project(pointIn3D); // convert to 2D

                genetic2DLayerPath1.Add(pointIn2D);

            }


            foreach (int[,] arr in pathChromosom2)
            {

                Point3D pointIn3D = new Point3D(cellsCenters[0, arr[0, 0]], cellsCenters[1, arr[1, 0]], 700);

                PointF pointIn2D = Project(pointIn3D); // convert to 2D

                genetic2DLayerPath2.Add(pointIn2D);

            }


            foreach (int[,] arr in pathChromosom3)
            {

                Point3D pointIn3D = new Point3D(cellsCenters[0, arr[0, 0]], cellsCenters[1, arr[1, 0]], 700);

                PointF pointIn2D = Project(pointIn3D); // convert to 2D

                genetic2DLayerPath3.Add(pointIn2D);

            }


            foreach (int[,] arr in pathChromosom4)
            {

                Point3D pointIn3D = new Point3D(cellsCenters[0, arr[0, 0]], cellsCenters[1, arr[1, 0]], 700);

                PointF pointIn2D = Project(pointIn3D); // convert to 2D

                genetic2DLayerPath4.Add(pointIn2D);

            }



            foreach (int[,] arr in pathChromosom5)
            {

                Point3D pointIn3D = new Point3D(cellsCenters[0, arr[0, 0]], cellsCenters[1, arr[1, 0]], 700);

                PointF pointIn2D = Project(pointIn3D); // convert to 2D

                genetic2DLayerPath5.Add(pointIn2D);

            }

            foreach (int[,] arr in pathChromosom6)
            {

                Point3D pointIn3D = new Point3D(cellsCenters[0, arr[0, 0]], cellsCenters[1, arr[1, 0]], 700);

                PointF pointIn2D = Project(pointIn3D); // convert to 2D

                genetic2DLayerPath6.Add(pointIn2D);

            }

            int counter = 0;

            for (int i = 0; i < 6; i++)
            {


                if (i == 0) // first chromosome
                {
                     xPoints = new float[genetic2DLayerPath1.Count()];

                     yPoints = new float[genetic2DLayerPath1.Count()];



                    auv[0].auvDepth = 700;


                    foreach(PointF p in genetic2DLayerPath1)
                    {

                        xPoints[counter] = p.X;

                        yPoints[counter] = p.Y;

                        counter++;

                    }

                    counter = 0;

                }


                if (i == 1) // second chromosome
                {
                     xPoints = new float[genetic2DLayerPath2.Count()];

                     yPoints = new float[genetic2DLayerPath2.Count()];



                    auv[0].auvDepth = 700;


                    foreach (PointF p in genetic2DLayerPath2)
                    {

                        xPoints[counter] = p.X;

                        yPoints[counter] = p.Y;

                        counter++;

                    }

                    counter = 0;

                }


                if (i == 2) // third chromosome
                {
                     xPoints = new float[genetic2DLayerPath3.Count()];

                     yPoints = new float[genetic2DLayerPath3.Count()];



                    auv[0].auvDepth = 700;


                    foreach (PointF p in genetic2DLayerPath3)
                    {

                        xPoints[counter] = p.X;

                        yPoints[counter] = p.Y;

                        counter++;

                    }


                    counter = 0;

                }

                if (i == 3) // fourth chromosome
                {
                     xPoints = new float[genetic2DLayerPath4.Count()];

                     yPoints = new float[genetic2DLayerPath4.Count()];



                    auv[0].auvDepth = 700;


                    foreach (PointF p in genetic2DLayerPath4)
                    {

                        xPoints[counter] = p.X;

                        yPoints[counter] = p.Y;

                        counter++;

                    }

                    counter = 0;

                }

                if (i == 4) // fifth chromosome
                {
                     xPoints = new float[genetic2DLayerPath5.Count()];

                     yPoints = new float[genetic2DLayerPath5.Count()];



                    auv[0].auvDepth = 700;


                    foreach (PointF p in genetic2DLayerPath5)
                    {

                        xPoints[counter] = p.X;

                        yPoints[counter] = p.Y;

                        counter++;

                    }

                    counter = 0;

                }


                if (i == 5) // sixth chromosome
                {
                    xPoints = new float[genetic2DLayerPath6.Count()];

                    yPoints = new float[genetic2DLayerPath6.Count()];



                    auv[0].auvDepth = 700;


                    foreach (PointF p in genetic2DLayerPath6)
                    {

                        xPoints[counter] = p.X;

                        yPoints[counter] = p.Y;

                        counter++;

                    }

                    counter = 0;

                }

                counter = 0;

                while (countNumOfPaths != 2)
                {


                    Thread.Sleep(900); // assume that it represents the speed of the AUV which is in our case = 3 m/s as each meter equal to 300 seconds in thread.sleep()  

                    if (auv0Genetic.CancellationPending)
                    {
                        e.Cancel = true;
                        return;
                    }


                    if (forward)
                    {


                        if (counter == xPoints.Length - 1)
                        {

                            backward = true;

                            forward = false;

                            countNumOfPaths++;


                        }


                        else
                        {

                            auv[0].auvX = xPoints[counter];

                            auv[0].auvY = yPoints[counter];

                            counter++;

                        }


                    }

                    if (backward)
                    {

                        if (counter == 0)
                        {

                            backward = false;

                            forward = true;

                            countNumOfPaths++;

                        }

                        else
                        {

                            auv[0].auvX = xPoints[counter];


                            auv[0].auvY = yPoints[counter];


                            counter--;
                        }

                    }



                    //////////////////////// Draw ///////////////////////////

                    iSetupDisplay = 0;

                    if (iSetupDisplay != -1)
                    {
                        iSetupDisplay += 10;
                        if (iSetupDisplay >= topology.Width)
                            iSetupDisplay = -1;
                        topology.Refresh();
                    }


                    /////////////////////////////////////////////////////////

                }



            }

        }

The problem is that the workers run only once and then I got the execution stopped with the following error:

This BackgroundWorker is currently busy and cannot run multiple tasks concurrently.

enter image description here

Note: I have tried to create new background worker each time but it did not work correctly and I got the execution out of response as I declared 50x4 background workers!!.

The background workers are registered as follows:

private System.ComponentModel.BackgroundWorker auv0Genetic;
private System.ComponentModel.BackgroundWorker auv1Genetic;
private System.ComponentModel.BackgroundWorker auv2Genetic;
private System.ComponentModel.BackgroundWorker auv3Genetic;



this.auv0Genetic = new System.ComponentModel.BackgroundWorker();
this.auv1Genetic = new System.ComponentModel.BackgroundWorker();
this.auv2Genetic = new System.ComponentModel.BackgroundWorker();
this.auv3Genetic = new System.ComponentModel.BackgroundWorker();
Rose
  • 349
  • 3
  • 17
  • 4
    I would not use BackgroundWorker for this, I would use `Task.Run` instead to spin up the 4 jobs. Also, the code at the end of your function really should be in a `lock` statement, you have multiple threads updating shared numbers at the same time. – Scott Chamberlain Sep 01 '17 at 20:30
  • So how to apply your answer, I know the way of using background workers :( could you help me to modify them?!! – Rose Sep 01 '17 at 20:39
  • Your main loop is just firing away, not waiting for the BackgroundWorkers to finish... – C. Gonzalez Sep 01 '17 at 21:01
  • @C.Gonzalez : how to let this loop wait each iteration for the background workers ? – Rose Sep 01 '17 at 21:31

2 Answers2

1

Converting your original example to use TPL, your code would look something like this.

CancellationTokenSource cancelSource = new CancellationTokenSource();

public void Cancel() {
    cancelSource.Cancel();
}

public async Task Genetic_Algorithm(List<int[,]> population) {
    cancelSource = new CancellationTokenSource();
    DateTime startT = DateTime.Now.Date;
    double[,] FitnessValue = new double[6, 2]; // for all five chromosome we store two Values the Fitness Value and the Fitness Ratio
    int[] RouletteWheel = new int[6];
    int round = 0;
    for (geneticIteration = 0; geneticIteration < 50; geneticIteration++) {
        round = geneticIteration + 1;
        // Calculate the fitness Function and the Fitness Ratio
        await FitnessFunctionAsync(population, cancelSource.Token); // Fitness Function
    }
    MessageBox.Show("Press Again");
} 

public Task FitnessFunctionAsync(List<int[,]> population, CancellationToken cancelToken = default(CancellationToken)) {
    extractPath(population, geneticIteration);

    var task0 = RunAuv0GeneticAsync(cancelToken); // start obj # 1
    var task1 = RunAuv1GeneticAsync(cancelToken); // start obj # 2
    var task2 = RunAuv2GeneticAsync(cancelToken); // start obj # 3
    var task3 = RunAuv3GeneticAsync(cancelToken); // start obj # 4

    return Task.WhenAll(task0, task1, task2, task3);
}

In this hypothetical example the background works' do work methods would also be converted to be async as well.

For example

async Task RunAuv0GeneticAsync(CancellationToken cancelToken = default(CancellationToken)) {
    //Code removed for brevity
    countNumOfPaths = 0;

    //...code removed for brevity

    int counter = 0;

    for (int i = 0; i < 6; i++) {

        //...code removed for brevity

        counter = 0;

        while (countNumOfPaths != 2) {
            await Task.Delay(900);
            if (cancelToken != null && cancelToken.IsCancellationRequested) {
                return;
            }

            //...code removed for brevity;
        }

        //...code removed for brevity
    }
}

That way each call to FitnessFunction is awaited per iteration.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Nice, I will tried it but I have VS 2010 .. so how to use your answer ? – Rose Sep 13 '17 at 19:55
  • What is the necessary declaration as I got an error on the method name of " RunAuv0GeneticAsync" , "WhenAll()" and Task.Delay()... saying that are not known or identified !! or missing references while I put all the needed references. I've switched to VS 2012 ... – Rose Sep 14 '17 at 08:16
  • @Rose Make sure you have the necessary namespace `System.Threading.Tasks` in order to to be able to use `Task` and `async/await` – Nkosi Sep 14 '17 at 09:35
  • @Rose can you also clarify if the workers in `FitnessFunction` were suppose to execute simultaneously or sequentially? – Nkosi Sep 14 '17 at 09:40
  • @Rose Ok then my advice to use `Task.WhenAll` was correct. I wasn't sure if I had understood what you wanted. – Nkosi Sep 14 '17 at 16:08
  • You can look to this post as it contains more details : https://stackoverflow.com/questions/46149009/how-to-use-task-run-to-perform-the-function-of-do-work-of-a-background-worker/46203391?noredirect=1#comment79409659_46203391 – Rose Sep 14 '17 at 16:11
  • @Noksi : I got 3 errors : [1] The call is ambiguous between the following methods or properties: 'AwaitExtensions.GetAwaiter(System.Threading.Tasks.Task)' and 'AsyncCtpThreadingExtensions.GetAwaiter(System.Threading.Tasks.Task)' – Rose Sep 15 '17 at 19:44
  • @Noksi : [2] 'Task' does not contain a definition for 'WhenAll' – Rose Sep 15 '17 at 19:45
  • @Noksi : 'Task' does not contain a definition for 'Delay' – Rose Sep 15 '17 at 19:46
  • @Rose, Looks like you have some conflicting references. You could consider using the full name space to avoid the naming conflict. – Nkosi Sep 16 '17 at 00:37
  • I used : using System.Threading; using System.Threading.Tasks; using System.Diagnostics; – Rose Sep 16 '17 at 03:52
  • Kindly make help, also there is a problem with Task.Delay(900) – Rose Sep 16 '17 at 03:53
0

With BackgroundWorker it is tricky to coordinate different work items, easier to do with Task, as mentioned in the comments.

A minimal example would be as below, where every iteration of the loop uses Task.WaitAll() to make sure the work is done before starting a new batch.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;

namespace TaskExample
{
    public partial class Form1 : Form
    {
        //BackgroundWorker bw01, bw02, bw03, bw04;
        const int nmbRuns = 10;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            for (int cnt = 0; cnt < nmbRuns; cnt++)
            {
                List<Task> allTasks = new List<Task>();
                List<int> elements = new List<int>() { 0, 1, 2, 3 };
                foreach (int i in elements)
                {
                    Task t = Task.Run(() => Work(i));
                    //Task t = Task.Run(() => { Task.Delay(i + 1000); Debug.WriteLine(i + " started"); });
                    allTasks.Add(t);
                }
                Debug.WriteLine("All workers started");
                Task.WaitAll(allTasks.ToArray());
                Debug.WriteLine("All workers finished run " + cnt);
            }
            Debug.WriteLine("All done.");
        }


        private async Task<bool> Work(int id)
        {
            Debug.WriteLine(id + " awaiting.");
            await Task.Delay(1000 * id);
            Debug.WriteLine(id + " finished waiting.");
            return true;
        }

    }
}
C. Gonzalez
  • 689
  • 7
  • 8
  • I was proposing a different approach, not a simple fix in your code. If you really need to use Backgroundworker, you need to put some condition in your for(genetic ... to check if the bw finished befor moving on. Also, your workers appear not to have OnRunWorkerCompleted handlers. In them you would signal that a specific bw finished and use AutoResetEevents for example. – C. Gonzalez Sep 02 '17 at 18:10
  • Hi, I've got an error on Task.Run & .work()--> not identified !! – Rose Sep 13 '17 at 00:25
  • @C.Gonzalez you are mixing blocking calls `.WaitAll` with async calls which can lead to deadlocks. Also Work already returns a Task so there is really no need to wrap it in a `Task.Run` that call `allTasks.Add(Work(i))` – Nkosi Sep 13 '17 at 12:35
  • Agreed about the unnecessary wrapping. About deadlocks, that´s for the OP to solve, right? He/she was looking for a way to synchronize 4 jobs and not allow them to progress until all four had finished within a loop. – C. Gonzalez Sep 13 '17 at 14:31