Scenario:
I want a task scheduler that enables my application to schedule some tasks in small time interval. I want to use it in my socket server application. For example, when the user connects to the socket application schedule some task in 15 seconds to disconnect user if no actions were made.
- Queuing task for specific time
- Ability to stop scheduled jobs
- Running jobs in separate thread
So, I've decided to create my own Task Schedule. It can schedule tasks. It use System.Timers.Timer to find tasks to run. I want you to review my decision, may be solution has architecture errors etc.
public abstract class ScheduleJob
{
//Date when task must be executed
public DateTime DateTrigger { get; set; }
//Task Id (for search and cancel)
public string TaskId { get; set; }
//For logs
public abstract string TaskName { get; }
//Main task method
protected abstract void OnStart();
//Default task class delay (for example 15 seconds)
public abstract TimeSpan ScheduleTo { get; }
private bool _isRunning = false;
public void Start()
{
_isRunning = true;
var s = new Stopwatch();
s.Start();
try
{
//run task in separated thread
Task.Factory.StartNew(OnStart).ContinueWith(t =>
{
s.Stop();
_isRunning = false;
//if (t.Exception != null)
//{
//TODO work with exceptions
//}
});
}
catch (Exception ex)
{
//TODO work with exceptions
}
}
}
Manager to work with jobs
public sealed class ScheduleManager : IDisposable
{
private static ScheduleManager _instance = new ScheduleManager();
public static ScheduleManager Instance { get { return _instance; } }
private List<ScheduleJob> _jobs;
private object _lock = new object();
private Timer _timer;
private ScheduleManager()
{
_jobs = new List<ScheduleJob>();
_timer = new Timer();
_timer.Interval = 1000;
_timer.Elapsed += TimerOnElapsed;
_timer.Start();
}
private void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
lock (_lock)
{
var tasks = _jobs.Where(x => x.DateTrigger <= DateTime.Now).ToList();
foreach (ScheduleJob scheduleJob in tasks)
{
scheduleJob.Start();
_jobs.Remove(scheduleJob);
}
}
}
public void Schedule(ScheduleJob job)
{
lock (_lock)
{
job.DateTrigger = DateTime.Now + job.ScheduleTo;
_jobs.Add(job);
}
}
public void Remove(string taskId)
{
lock (_lock)
{
_jobs.RemoveAll(x => x.TaskId.Equals(taskId, StringComparison.OrdinalIgnoreCase));
}
}
public void Dispose()
{
if (_timer != null)
{
_timer.Stop();
//todo stop or cancel jobs
_timer = null;
}
}
}
Example of job UserShouldSendAuthTokenAfterConnectToSocketTask
public class UserShouldSendAuthTokenAfterConnectToSocketTask : ScheduleJob
{
private string _connectionId;
public UserShouldSendAuthTokenAfterConnectToSocketTask(string connectionId)
{
_connectionId = connectionId;
TaskId = GenerateTaskId(_connectionId);
}
public static string GenerateTaskId(string connectionId)
{
return $"UserShouldSendAuthTokenAfterConnectToSocketTask_{connectionId}";
}
public override string TaskName { get { return "User should auth to socket"; } }
protected override void OnStart()
{
SocketServer.Instance.SendMessage(new UserShouldSendAuthTokenAfterConnectToSocket(), _connectionId);
}
public override TimeSpan ScheduleTo
{
get
{
return TimeSpan.FromSeconds(GameConfig.SecondsUserShouldSendAuthTokenAfterConnectToSocket);
}
}
}