Вечная операция многозадачности - PullRequest
1 голос
/ 06 января 2020

Я изучаю многозадачность, и я пришел к очень простому решению, используя windows форму. enter image description here

У меня есть список объектов с именем "Автомобиль":

public class Car
{
    public int Id { get; set; }
    public Status Status { get; set; }
}

public enum Status
{
    Running,
    Standby,
    Finished
}

, и я хочу поработать в "мультисессиях (задачах)" .

Итак, скажем, у моего "Автомобиля" есть два метода: StartFullEngine() и GoDrive(), которые я записал в CarBusiness.cs, и я хочу вывести некоторые сообщения в richtextbox. У меня есть фиксированный список из 10 автомобилей, и количество сессий будет представлять количество автомобилей, идущих на улицу (почти в одно и то же время), но когда они находятся на улице, они всегда будут делать одно и то же. Что:

  1. StartFullEngine(). После запуска, go до следующего шага:
  2. GoDrive(). который может выполняться асинхронно, в то время как другая задача выберет следующий автомобиль из списка и вернется к шагу 1. Он остается в этом l oop навсегда, но без сессий.

Для достижения вот что я сделал:

CarBusiness.cs:

public class CarBusiness
{
    private RichTextBox _richTextBox;
    private static readonly object syncLock = new object();
    private Car _carToRun;
    private Random random;
    public CarBusiness(Car car, RichTextBox richTextBox)
    {
        _carToRun = car;
        _richTextBox = richTextBox;
        random = new Random();
    }

    private void OutputMessage(string msg)
    {
        lock (syncLock)
        {
            _richTextBox.BeginInvoke((Action)(() =>
            {
                _richTextBox.AppendText($"#{_carToRun.Id}: [{DateTime.Now.Hour.ToString("D2")}:{DateTime.Now.Minute.ToString("D2")}:{DateTime.Now.Second.ToString("D2")}] - {msg}\r\n");
            }));

        }
    }

    public Task StartFullEngine()
    {
        return Task.Run(async () =>
        {
            var start = DateTime.Now;
            await Task.Delay(random.Next(1000, 10000));
            var end = DateTime.Now;
            TimeSpan timeDiff = start - end;
            var readableDiff = string.Format(
                "{0:D2} hrs, {1:D2} mins, {2:D2} secs",
                timeDiff.Hours, timeDiff.Minutes, timeDiff.Seconds);
            OutputMessage($"Full engined finished. Time ellapsed: {readableDiff}");
        });
    }

    public Task GoDrive()
    {
        return Task.Run(async () =>
        {
            var start = DateTime.Now;
            await Foo();
            Boo().Wait();
            var end = DateTime.Now;
            TimeSpan timeDiff = start - end;
            var readableDiff = string.Format(
                "{0:D2} hrs, {1:D2} mins, {2:D2} secs",
                timeDiff.Hours, timeDiff.Minutes, timeDiff.Seconds);
            OutputMessage($"GoDrive() finished. Time ellapsed: {readableDiff}");
        });
    }


    private Task Foo()
    {
        return Task.Run(async () =>
        {
            //long operation
            OutputMessage($"Running Foo()...");
            await Task.Delay(random.Next(1000, 3000));
            OutputMessage($"Foo() ended!");
        });
    }


    private Task Boo()
    {
        return Task.Run(async () =>
        {
            OutputMessage($"Running Boo()...");
            await Task.Delay(random.Next(10000, 30000));
            OutputMessage($"Boo() ended!");
        });
    }
}

CarDao.cs:

public class CarDao
{
    public List<Car> GetListOfCar()
    {
        var list = new List<Car>();
        for (int i = 1; i <= 10; i++)
            list.Add(new Car { Id = i, Status = Status.Standby });

        return list;
    }
}

Form1.cs:

public partial class Form1 : Form
{
    private List<Car> listOfCars;
    private object objInstances = new object();
    public Form1()
    {
        InitializeComponent();
    }
    private Task<Car> GetNextCar()
    {
        return Task.Run(() =>
        {
            lock (objInstances)
            {
                //Need a logic to prioritize getting cars that have never "run" before
                var nextCar = listOfCars.Where(x => x.Status != Status.Running).SkipWhile(x => x.Status == Status.Finished).FirstOrDefault();
                return nextCar;
            }
        });
    }
    private Task<List<Car>> GetInstances(Status statusCar)
    {
        return Task.Run(() =>
        {
            lock (objInstances)
            {
                return listOfCars.Where(x => x.Status == statusCar).ToList();
            }
        });
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        var carDao = new CarDao();

        //NumericUpDown value
        var maxSessions = numberOfSessions.Value;

        //fixed list of cars
        listOfCars = carDao.GetListOfCar();

        //Never stop running
        while (true)
        {
            var runningInstances = GetInstances(Status.Running).Result;

            for (int i = runningInstances.Count(); i < maxSessions; i++)
            {
                var carToRun = GetNextCar().Result;
                carToRun.Status = Status.Running;

                var carBusiness = new CarBusiness(carToRun, richTextBox);

                Task.Run(() =>
                {
                    //First thing: StartFullEngine()
                    carBusiness.StartFullEngine().Wait();
                }).Wait();

                //After started Engine, GoDrive() can run asynchronously.
                await Task.Run(() =>
               {
                   carBusiness.GoDrive().Wait();
                   carToRun.Status = Status.Finished;
               });
                //Next loop;
            }
            //logic to wait some tasks get done to start again
            runningInstances = GetInstances(Status.Running).Result;
            if (runningInstances.Count() == maxSessions)
                Task.Delay(20000).Wait();
        }

    }
}

и когда я бегу, это вывод через пару секунд: (кажется, что он работает не так, как я хочу). Дайте мне знать, если это недостаточно ясно.

enter image description here

После запуска Engine GoDrive() может работать асинхронно (go до следующей итерации независимо от конец GoDrive()).

Я ожидаю что-то вроде этого:

 #1: [00:23:27] - Full engined finished. Time ellapsed: 00 hrs, 00 mins, -05 secs
 //Once finished, it starts another task. (this case car #2). Now we have two tasks running, so we will until one of them finishes then get another car.
 #1: [00:23:27] - Running Foo()...
 #1: [00:23:29] - Foo() ended!
 #2: [00:23:29] - Full engined finished. Time ellapsed: 00 hrs, 00 mins, -02 secs
 #2: [00:23:29] - Running Foo()...
 #1: [00:23:29] - Running Boo()...
 #2: [00:23:31] - Running Boo()...
 #2: [00:23:32] - Boo() ended!
 #1: [00:23:52] - Boo() ended!
 #1: [00:24:15] - GoDrive() finished. Time ellapsed: 00 hrs, 00 mins, -25 secs
 //Now we have finished one task. Go to another (car #3)
 #2: [00:24:16] - GoDrive() finished. Time ellapsed: 00 hrs, 00 mins, -16 secs
 //Now we have finished one task. Go to another (car #4)
 #3: [00:24:20] - Full engined finished. Time ellapsed: 00 hrs, 00 mins, -05 secs
 #3: [00:24:21] - Running Foo()...
 #3: [00:24:25] - Foo() ended!
 #4: [00:23:27] - Full engined finished. Time ellapsed: 00 hrs, 00 mins, -05 secs
 #3: [00:24:29] - Running Foo()...
 #4: [00:23:29] - Running Boo()...
 #4: [00:23:31] - Boo() ended!
...