Basisklasse für die Implementierung von wiederkehrenden Tasks während der Programm Ausführung.
Hierbei wird auf Threads, BackgroundWorker und Timer Objekte verzichtet.
Beispiel Program:
namespace Snippets.Net
{
using System;
using System.Collections.Generic;
class ExampleProgram
{
static void Main(string[] args)
{
var jobs = new List<ISchedulerJob>();
jobs.Add(new SchedulerJobA());
jobs.Add(new SchedulerJobB());
jobs.ForEach(x => x.Start());
Console.ReadLine();
jobs.ForEach(x => x.Stop());
}
}
public class SchedulerJobA : SchedulerJobBase
{
protected override void OnDispose()
{
}
protected override void OnExecution()
{
WriteToConsole($"{ GetType().Name} Warning");
}
protected override void OnInstanceCreated()
{
Delay = TimeSpan.FromSeconds(8);
Interval = TimeSpan.FromSeconds(16);
}
}
public class SchedulerJobB : SchedulerJobBase
{
protected override void OnDispose()
{
}
protected override void OnExecution()
{
WriteToConsole($"{GetType().Name} Information");
}
protected override void OnInstanceCreated()
{
Delay = TimeSpan.FromSeconds(5);
Interval = TimeSpan.FromSeconds(3);
}
}
}
Beispiel Ausgabe:
[13:50:18] Scheduler Job 'SchedulerJobA' started. Recurring every 16 seconds, after a delay of 00:00:07.9927205.
[13:50:18] Scheduler Job 'SchedulerJobB' started. Recurring every 03 seconds, after a delay of 00:00:04.9834796.
[13:50:23] SchedulerJobB - Calling OnExecution Method
[13:50:23] SchedulerJobB Information
[13:50:26] SchedulerJobB - Calling OnExecution Method
[13:50:26] SchedulerJobA - Calling OnExecution Method
[13:50:26] SchedulerJobB Information
[13:50:26] SchedulerJobA Warning
[13:50:29] SchedulerJobB - Calling OnExecution Method
[13:50:29] SchedulerJobB Information
[13:50:32] SchedulerJobB - Calling OnExecution Method
[13:50:32] SchedulerJobB Information
[13:50:35] SchedulerJobB - Calling OnExecution Method
[13:50:35] SchedulerJobB Information
[13:50:38] SchedulerJobB - Calling OnExecution Method
[13:50:38] SchedulerJobB Information
[13:50:41] SchedulerJobB - Calling OnExecution Method
[13:50:41] SchedulerJobB Information
[13:50:42] SchedulerJobA - Calling OnExecution Method
[13:50:42] SchedulerJobA Warning
[13:50:44] SchedulerJobB - Calling OnExecution Method
[13:50:44] SchedulerJobB Information
[13:50:47] SchedulerJobB - Calling OnExecution Method
[13:50:47] SchedulerJobB Information
namespace Snippets.Net
{
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
public interface ISchedulerJob : IDisposable
{
CancellationTokenSource CancellationTokenSource { get; }
TimeSpan? Delay { get; set; }
DateTime? EndTime { get; set; }
TimeSpan Interval { get; set; }
DateTime? StartTime { get; set; }
bool IsActive { get; }
void Start();
void Stop();
}
public abstract class SchedulerJobBase : ISchedulerJob
{
#region Fields
private bool _disposed = false;
private CancellationTokenSource _tokenSource;
private DateTime? _startTime;
private DateTime? _endTime;
private bool _isActive;
private TimeSpan _interval;
private readonly Stopwatch _stopWatch;
#endregion
#region Properties
public CancellationTokenSource CancellationTokenSource => _tokenSource;
public TimeSpan? Delay
{
get
{
if (!_startTime.HasValue) return null;
return _startTime.Value - DateTime.Now;
}
set
{
if (value.HasValue) _startTime = DateTime.Now.Add(value.Value);
}
}
public DateTime? EndTime
{
get
{
return _endTime;
}
set
{
_endTime = value;
}
}
public TimeSpan Interval
{
get
{
return _interval;
}
set
{
_interval = value;
}
}
public bool IsActive => _isActive;
public DateTime? StartTime
{
get
{
return _startTime;
}
set
{
_startTime = value;
}
}
#endregion
#region Constructor
protected SchedulerJobBase() : base()
{
OnInstanceCreated();
_tokenSource = new CancellationTokenSource();
_isActive = false;
_stopWatch = new Stopwatch();
}
#endregion
#region Methods
public async void Start()
{
if (_isActive) return;
WriteToConsole($"Scheduler Job '{GetType().Name}' started. Recurring every {Interval:ss} seconds, after a delay of {Delay}.");
_isActive = true;
var currentDelay = TimeSpan.Zero;
if (_startTime.HasValue) currentDelay = _startTime.Value - DateTime.Now;
if (currentDelay <= TimeSpan.Zero) currentDelay = TimeSpan.Zero;
await Task.Delay(currentDelay, _tokenSource.Token);
TimeSpan newInterval;
while ((!_tokenSource.IsCancellationRequested))
{
_stopWatch.Restart();
try
{
WriteToConsole($"{GetType().Name} - Calling OnExecution Method");
await Task.Run(new Action(OnExecution), _tokenSource.Token);
}
catch (Exception ex)
{
WriteToConsole($"Error - {GetType().Name}");
WriteToConsole(ex.ToString());
}
finally
{
_stopWatch.Stop();
}
newInterval = _interval - _stopWatch.Elapsed;
if (newInterval < TimeSpan.Zero) newInterval = _interval;
if (_endTime.HasValue && _endTime.Value < DateTime.Now) Stop();
await Task.Delay(newInterval, _tokenSource.Token);
}
}
public void Stop()
{
if (!_isActive) return;
Console.WriteLine($"{GetType().Name} stopped");
_tokenSource.Cancel();
_isActive = false;
}
protected abstract void OnInstanceCreated();
protected abstract void OnExecution();
protected abstract void OnDispose();
protected void WriteToConsole(string message)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] " + message);
}
#endregion
#region IDisposable Support
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_tokenSource?.Dispose();
OnDispose();
}
_disposed = true;
}
}
void IDisposable.Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}
Kommentare zum Snippet