创建自定义且可靠的任务计划程序

时间:2018-10-05 10:39:46

标签: c# .net memory .net-core scheduler

我正在尝试创建自己的C#任务计划程序,因此,例如,我想在一周的每个星期一使用ID参数运行特定的void。我还想保留每个调度程序的所有正在运行任务的列表。

因此,您将拥有一个包含任务列表的调度程序,并且这些任务具有操作和触发器,例如,操作是我要执行的方法,而触发器是例如每周的每个星期一。

现在,当任务完成并且已经到了结束日期时,它就不得不像以前不存在的那样自行处置。这是我不知道该怎么办的地方。

现在这是一个极端的例子,但是我尝试调度100万个任务,这些任务将在10秒后运行。所有任务都已执行,但不知何故未正确处理。 Visual Studio表示,任务自行处理后,进程内存约为700 MB,堆内存约为2 MB。

我尝试了两件事:

  • 刷新系统,每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 :(得分:0)

我将使用Quartz.NET和/或Hangfire作为调度程序解决方案。

https://www.quartz-scheduler.net

https://www.hangfire.io