我正在尝试创建自己的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);
}
}
}
请记住,这只是一个原型,因此它还不包含整个触发器和动作系统以及我提到的其他内容。
答案 0 :(得分:0)