创建自定义且可靠的任务调度程序 [英] Creating a custom and reliable task scheduler

查看:23
本文介绍了创建自定义且可靠的任务调度程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建自己的 C# 任务调度程序,例如,我想在每周一的每个星期一运行一个带有 Id 参数的特定 void.我还想为每个调度程序保留所有正在运行的任务的列表.

I'm trying to create my own C# task scheduler, so for example I want to run a specific void with an Id argument every Monday of the week. I also want to keep a list of all running tasks per scheduler.

所以你会有一个包含任务列表的调度程序,这些任务有动作和触发器,动作是我想要执行的方法,触发器是例如每周的每个星期一.

So you would have a scheduler that contains a list of tasks and those tasks have actions and triggers, actions being the method(s) that I want to execute and triggers being for example every Monday of every week.

现在,当任务完成并到达结束日期时,它必须像从未存在过一样处理自己.这是我不知道该怎么办的地方了.

Now when the task is done and it has reached it's end date it has to pretty much dispose itself like it never existed. This is where I don't know what to do anymore.

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

Now this is an extreme example but I tried scheduling one million tasks that would run after 10 seconds. All the tasks ran but somehow were not disposed correctly. Visual Studio said that the Process Memory was about 700 MB and the Heap Memory about 2 MB after the tasks have disposed themselves.

我尝试了两件事:

  • 一个刷新系统,每 30 秒运行一次,缓冲已完成的任务,并将它们从列表中删除,然后从缓冲区中删除.这有点工作,在运行一百万个任务后,它会给我一个集合被修改"异常.

  • A flush system that runs every 30 seconds and buffers finished tasks and removes them from the list and then from the buffer. This worked kinda, after running one million tasks it would give me a "Collection was modified" exception.

自处理任务,当任务完成后会自行处理.当运行 10 万个任务时,它会处理掉大部分任务并将它们从列表中删除,但我至少还有 5000 个任务仍在任务列表中.

Self disposing tasks, when the task is finished it will dispose of itself. When running this with one hundred thousand tasks it would dispose most of them and remove them from the list but I had at least five thousand tasks still in the task list.

我的问题是如何正确可靠地处理任务并将它们从任务列表中删除,以便它们不再存在于内存中而不会出现任何异常,例如集合已修改".

My question is how do I correctly and reliably dispose the tasks and remove them from the task list so that they are no longer existing within the memory without getting any exceptions such as "Collection was modified".

这是我使用的代码,您可能需要对其进行一些编辑以使其使用冲洗系统和自处理系统.

Here is my code that I used, you might need to edit it a little to make it use the flush system and the self disposing system.

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);

        }
    }
}

请记住,这只是一个原型,因此它不包含整个触发器和动作系统以及我提到的其他内容.

Keep in mind this is just a prototype so it doesn't contain the whole trigger and action system yet and other things I mentioned.

推荐答案

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

I will use Quartz.NET and or Hangfire as scheduler solution.

https://www.quartz-scheduler.net

https://www.hangfire.io

这篇关于创建自定义且可靠的任务调度程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆