Создание настраиваемого и надежного планировщика задач - PullRequest
0 голосов
/ 05 октября 2018

Я пытаюсь создать свой собственный планировщик задач C #, поэтому, например, я хочу запускать определенный void с аргументом Id каждый понедельник недели.Я также хочу сохранить список всех запущенных задач для каждого планировщика.

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

Теперь, когда задача выполнена и она достигла своей конечной даты, она должна в значительной степени распоряжаться собой, как будто ее никогда не существовало.Здесь я не знаю, что делать дальше.

Сейчас это крайний пример, но я попытался составить расписание для одного миллиона задач, которые будут выполняться через 10 секунд.Все задачи выполнялись, но как-то не были правильно расположены.Visual Studio сообщает, что объем памяти процесса составляет около 700 МБ, а памяти кучи - около 2 МБ после выполнения задач.

Я попробовал две вещи:

  • Сброссистема, которая запускается каждые 30 секунд и буферизует завершенные задачи и удаляет их из списка, а затем из буфера.Это сработало отчасти, после запуска миллиона задач мне бы выдалось исключение «Коллекция была изменена».

  • Самоораспределение задач, когда задача будет завершена, она избавится от себя.При выполнении этого с сотней тысяч задач он избавился бы от большинства из них и удалил бы их из списка, но у меня было по крайней мере пять тысяч задач в списке задач.

Мой вопрос заключается в том, какправильно ли я правильно и надежно располагаю задачи и удаляю их из списка задач, чтобы они больше не существовали в памяти без получения каких-либо исключений, таких как «Коллекция была изменена».

Вот мой код, который я использовалВам, возможно, придется немного отредактировать его, чтобы использовать в нем систему сброса и самоуничтожающуюся систему.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using static TaskScheduler.Scheduler;

namespace TaskScheduler
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            Scheduler scheduler = new Scheduler(new TimeSpan(0, 0, 30));

            for (int i = 0; i < 100000; i++)
            {
                scheduler.Schedule(Function, new Settings() { Id = i, Start = DateTime.Now.AddSeconds(10) });
            }

            scheduler.Schedule(Function, new Settings() { Id = 1123, Recurring = true, Start = DateTime.Now.AddSeconds(5), End = DateTime.Now.AddDays(14) });

            while (true)
            {
                Console.WriteLine(scheduler.Tasks.Count());

                System.Threading.Thread.Sleep(500);
            }
        }

        public static void Function(Task task)
        {
            Console.WriteLine($"Test function: {task._settings.Id}");
        }
    }

    public class Scheduler : IDisposable
    {
        public List<Task> Tasks = new List<Task>();
        public List<Task> FlushCollection = new List<Task>();

        private Timer timer; //Flush timer

        public Scheduler(TimeSpan time)
        {
            timer = new Timer(time.TotalMilliseconds);
            timer.Elapsed += new ElapsedEventHandler(Flush);

            timer.Start();
        }

        public void Flush(object sender, ElapsedEventArgs args)
        {
            foreach (Task task in Tasks.ToArray())
            {
                if (task.timer == null)
                {
                    FlushCollection.Add(task);
                }

            }

            foreach(Task task in FlushCollection.ToArray())
            {
                Tasks.Remove(task);
            }

            FlushCollection.Clear();
        }

        public void Schedule(Action<Task> action, Settings settings)
        {
            Tasks.Add(new Task(this, action, settings));
        }

        public void Unschedule(Task task)
        {
            task.Dispose();

            Tasks.Remove(task);
        }

        public void Unschedule(int id)
        {
            Unschedule(Tasks.Where(x => x._settings.Id == id).FirstOrDefault());
        }

        public void Dispose()
        {
            foreach (Task task in Tasks.ToArray())
            {
                task.Dispose();
            }

            Tasks.Clear();
        }

        public class Task : IDisposable
        {
            public Scheduler _scheduler;

            public Action<Task> _action;
            public Settings _settings;

            public Timer timer;
            private DateTime next;

            public Task(Scheduler scheduler, Action<Task> action, Settings settings)
            {
                _scheduler = scheduler;

                _action = action;
                _settings = settings;

                Init();
            }

            public void Init()
            {
                next = DateTime.Now + _settings.Interval;

                timer = new Timer((_settings.Start - DateTime.Now).TotalMilliseconds);
                timer.Elapsed += new ElapsedEventHandler(Elapsed);

                timer.Start();

                if (_settings.Interval.TotalMilliseconds != 0)
                {
                    timer.Interval = _settings.Interval.TotalMilliseconds;
                }
            }

            public void Elapsed(object sender, ElapsedEventArgs args)
            {
                if (!Ready())
                {
                    return;
                }

                Run();
            }

            public void Dispose()
            {
                timer.Dispose();
                timer = null;
            }

            public bool Ready()
            {
                return DateTime.Now >= next;
            }

            public void Run()
            {
                _action(this);

                if (Expired() || !_settings.Recurring)
                {
                    _scheduler.Unschedule(this);
                }
            }

            public bool Expired()
            {
                if (DateTime.Now >= _settings.End)
                {
                    return true;
                }

                return false;
            }
        }

        public class Settings
        {

            public int? Id { get; set; }

            public bool Recurring { get; set; } = false;

            public TimeSpan Interval { get; set; } //Not required when not recurring.

            public DateTime Start { get; set; } = DateTime.Now;
            public DateTime End { get; set; } = DateTime.Now.AddTicks(1);

        }
    }
}

Имейте в виду, что это всего лишь прототип, поэтому он не содержит всего триггера и действияСистема еще и другие вещи, которые я упомянул.

1 Ответ

0 голосов
/ 05 октября 2018

Я буду использовать Quartz.NET и / или Hangfire в качестве решения для планировщика.

https://www.quartz -scheduler.net

https://www.hangfire.io

...