Как использовать несколько фоновых рабочих в цикле, чтобы начать новую работу каждую итерацию для каждого фонового рабочего в C#
Мне нужно использовать 4 фоновых рабочих, чтобы перемещать 4 объекта по определенным координатам параллельно, т.е. запускать их вместе и останавливать вместе.
Я написал цикл для цикла 50 раз, и каждый раз, когда мне нужно запустить рабочих и после того, как они завершат свою работу, как в Do_Work()
метод остановить его и запустить их снова на следующей итерации, я написал следующие методы для вызова рабочих:
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
}
Их 4 метода Do_Work()
для 4 второстепенных работников следующее является одним из них:
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();
}
/////////////////////////////////////////////////////////
}
}
}
Проблема в том, что рабочие запускаются только один раз, а затем я остановил выполнение со следующей ошибкой:
Этот BackgroundWorker в настоящее время занят и не может запускать несколько задач одновременно.
Примечание: я пытался каждый раз создавать нового фонового работника, но он не работал должным образом, и я получил ответ из-за того, что объявил фоновых работников 50х4!!
Фоновые работники регистрируются следующим образом:
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();
2 ответа
Если преобразовать исходный пример в TPL, ваш код будет выглядеть примерно так.
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);
}
В этом гипотетическом примере методы фоновых работ также будут преобразованы в асинхронные.
Например
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
}
}
Таким образом, каждый звонок FitnessFunction
ожидается за итерацию.
С BackgroundWorker сложно координировать различные рабочие элементы, что проще сделать с помощью Task, как указано в комментариях.
Минимальный пример будет таким, как показано ниже, где каждая итерация цикла использует Task.WaitAll(), чтобы убедиться, что работа выполнена до запуска нового пакета.
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;
}
}
}