Skip to content

Instantly share code, notes, and snippets.

@rianjs
Created August 3, 2020 17:14
Show Gist options
  • Save rianjs/6832e6ee8e398552a81991e1a47aa0f8 to your computer and use it in GitHub Desktop.
Save rianjs/6832e6ee8e398552a81991e1a47aa0f8 to your computer and use it in GitHub Desktop.
Loop services framework in C#
using System.Threading.Tasks;
namespace Scratch
{
public interface IWorker
{
Task DoWorkAsync();
}
}
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Scratch
{
public class LoopAgent
{
public string Name { get; }
private readonly IWorker _worker;
private readonly TimeSpan _loopDelay;
private readonly CancellationToken _ct;
private readonly ILogger _logger;
/// <param name="loopDelay">Duration to wait before running the loop again. If the loop is meant to run every 3 seconds, and the previous iteration took
/// 2 seconds, the wait will be 1 second. If the previous iteration took 4 seconds, the wait will be zero.</param>
public LoopAgent(string name, IWorker worker, TimeSpan loopDelay, CancellationToken ct, ILogger logger)
{
Name = name;
_worker = worker;
_loopDelay = loopDelay > TimeSpan.Zero
? loopDelay
: TimeSpan.Zero;
_ct = ct;
_logger = logger;
}
public async Task LoopAsync()
{
while (!_ct.IsCancellationRequested)
{
_logger.LogInformation($"Starting {Name} work loop");
var timer = Stopwatch.StartNew();
try
{
await _worker.DoWorkAsync();
}
catch (Exception e)
{
_logger.LogError(e, "Loop threw an exception");
}
timer.Stop();
_logger.LogInformation($"Finished {Name} work loop in {timer.Elapsed}");
var sleepDelay = GetSleepDelay(_loopDelay, timer.Elapsed);
await Task.Delay(sleepDelay);
}
}
internal static TimeSpan GetSleepDelay(TimeSpan delay, TimeSpan elapsed)
{
var toWait = delay - elapsed;
return toWait > TimeSpan.Zero
? toWait
: TimeSpan.Zero;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace Scratch
{
public class PrintTime : IWorker
{
private int counter = 0;
public async Task DoWorkAsync()
{
Console.WriteLine($"{counter++:N0})\t{DateTime.UtcNow:O}");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace Scratch
{
class Program
{
private static CancellationTokenSource _cts = new CancellationTokenSource();
private static ILogger _logger = new NullLogger<Program>();
public static async Task Main()
{
var oneSecond = new PrintTime();
var threeSeconds = new PrintTime();
var loopAgents = new List<LoopAgent>
{
new LoopAgent("1 second delay", oneSecond, TimeSpan.FromSeconds(1), _cts.Token, _logger),
new LoopAgent("3 second delay", threeSeconds, TimeSpan.FromSeconds(3), _cts.Token, _logger),
};
await Task.WhenAll(loopAgents.Select(a => a.LoopAsync()));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment