Periodieke taken en timers
Plan terugkerend werk met PeriodicTimer en System.Threading.Timer en beheer annulering op een nette manier.
Periodieke taken en timers is een gratis C# Academy-les op CoddyKit. Dit is les 3 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject C# Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus C# Academy bevat in totaal 4 lessen.
Terugkerend werk plannen
Veel achtergrondservices moeten volgens een vast schema worden uitgevoerd — elke 5 minuten, elk uur of dagelijks. .NET biedt verschillende mechanismen: PeriodicTimer (modern), System.Threading.Timer (klassiek) en Quartz.NET (op cron gebaseerd).
PeriodicTimer: de moderne keuze
PeriodicTimer (geïntroduceerd in .NET 6) is ontworpen voor gebruik in async-methoden. De timer wordt precies volgens schema geactiveerd, zonder callbacks, en werkt goed samen met annulering.
public class MetricsCollectorService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken ct)
{
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
while (await timer.WaitForNextTickAsync(ct))
{
// Runs every 60 seconds
await CollectMetricsAsync(ct);
}
// Loop exits cleanly when ct is cancelled
}
}PeriodicTimer tegenover een Task.Delay-lus
PeriodicTimer is beter dan een lus met Task.Delay: de timer wordt met vaste tussenpozen vanaf de starttijd geactiveerd (niet vanaf de starttijd plus verwerkingstijd), waardoor afwijking na veel herhalingen wordt voorkomen.
// Task.Delay loop — drifts! (interval = delay + work time)
while (!ct.IsCancellationRequested)
{
await DoWorkAsync(ct); // takes 2 seconds
await Task.Delay(5000, ct); // actual period: 7 seconds
}
// PeriodicTimer — no drift
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));
while (await timer.WaitForNextTickAsync(ct))
{
await DoWorkAsync(ct); // period is still 5 seconds from start
}System.Threading.Timer
System.Threading.Timer is een klassieke threadpooltimer die een terugbelfunctie gebruikt. Deze ondersteunt zowel periodieke als eenmalige uitvoeringen. Let op: de terugbelfunctie wordt uitgevoerd op een threadpoolthread zonder ondersteuning voor async.
public class LegacyTimerService : BackgroundService, IDisposable
{
private Timer? _timer;
protected override Task ExecuteAsync(CancellationToken ct)
{
// Fire immediately then every 10 seconds
_timer = new Timer(DoWork, null,
dueTime: TimeSpan.Zero,
period: TimeSpan.FromSeconds(10));
return Task.CompletedTask;
}
private void DoWork(object? state)
{
// Runs on thread pool — avoid async void here
Console.WriteLine($"Timer fired: {DateTime.UtcNow}");
}
public override void Dispose() { _timer?.Dispose(); base.Dispose(); }
}Eenmalige vertraagde uitvoering
Gebruik PeriodicTimer met één tik, of combineer CancellationTokenSource.CancelAfter met Task.Delay, voor eenmalige vertraagde uitvoering.
// Run once after a delay
public async Task ScheduleOnceAsync(TimeSpan delay, Func<Task> action, CancellationToken ct)
{
await Task.Delay(delay, ct);
await action();
}
// Or with CancellationToken:
var cts = new CancellationTokenSource(TimeSpan.FromHours(1));
try
{
await Task.Delay(Timeout.Infinite, cts.Token);
}
catch (OperationCanceledException)
{
await RunScheduledJobAsync();
}PeriodicTimer met foutafhandeling
Wikkel het werk bij elke tik altijd in try/catch, zodat één fout niet verhindert dat de timer in de volgende cyclus opnieuw tikt.
protected override async Task ExecuteAsync(CancellationToken ct)
{
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(5));
while (await timer.WaitForNextTickAsync(ct))
{
try
{
await SyncInventoryAsync(ct);
}
catch (Exception ex)
{
// Log but don't crash — next tick will retry
_logger.LogError(ex, "Inventory sync failed");
}
}
}Uitlijnen op klokgrenzen
Als je het werk op specifieke tijdstippen wilt uitvoeren (bijvoorbeeld elk uur op het hele uur), bereken je de aanvankelijke vertraging tot de volgende klokgrens voordat je de timer start.
protected override async Task ExecuteAsync(CancellationToken ct)
{
// Wait until the next full hour
var now = DateTime.UtcNow;
var nextHour = now.Date.AddHours(now.Hour + 1);
var initialDelay = nextHour - now;
await Task.Delay(initialDelay, ct);
// Then tick every hour exactly
using var timer = new PeriodicTimer(TimeSpan.FromHours(1));
await RunJobAsync(ct); // run at the first tick
while (await timer.WaitForNextTickAsync(ct))
await RunJobAsync(ct);
}Bescherming tegen gelijktijdige tikken
Als werk langer kan duren dan één timerperiode, voorkom je gelijktijdige uitvoering met een SemaphoreSlim of sla je de tik over als het vorige werk nog loopt.
private readonly SemaphoreSlim _lock = new(1, 1);
protected override async Task ExecuteAsync(CancellationToken ct)
{
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(30));
while (await timer.WaitForNextTickAsync(ct))
{
// Skip if previous tick is still running
if (!await _lock.WaitAsync(0, ct))
{
_logger.LogWarning("Skipping tick — previous still running");
continue;
}
try { await DoWorkAsync(ct); }
finally { _lock.Release(); }
}
}Willekeurige spreiding voor gedistribueerde services
Wanneer meerdere instanties van een service gelijktijdig starten, voorkomt een willekeurige afwijking in de aanvankelijke vertraging stormloopproblemen bij gedeelde resources.
protected override async Task ExecuteAsync(CancellationToken ct)
{
// Add jitter: 0-5 seconds random delay on startup
var jitter = TimeSpan.FromSeconds(Random.Shared.NextDouble() * 5);
await Task.Delay(jitter, ct);
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
while (await timer.WaitForNextTickAsync(ct))
await RunJobAsync(ct);
}Uit de praktijk: cacheservice vernieuwen
Een periodieke service die elke 10 minuten een gedistribueerde cache vernieuwt, met willekeurige spreiding en foutherstel.
public class CacheRefreshService : BackgroundService
{
private readonly IServiceScopeFactory _factory;
private readonly ILogger<CacheRefreshService> _log;
protected override async Task ExecuteAsync(CancellationToken ct)
{
// Spread startups across instances
await Task.Delay(TimeSpan.FromSeconds(Random.Shared.Next(0, 30)), ct);
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(10));
while (await timer.WaitForNextTickAsync(ct))
{
try
{
using var scope = _factory.CreateScope();
var cache = scope.ServiceProvider.GetRequiredService<ICacheService>();
await cache.RefreshAllAsync(ct);
_log.LogInformation("Cache refreshed at {Time}", DateTime.UtcNow);
}
catch (Exception ex) { _log.LogError(ex, "Cache refresh failed"); }
}
}
}Korte controle
Waarom heeft PeriodicTimer de voorkeur boven een Task.Delay-lus voor terugkerende taken?
Samenvatting: periodieke taken en timers
Belangrijkste punten:
PeriodicTimer: async, houdt rekening met annulering en veroorzaakt geen afwijking — de moderne keuzeSystem.Threading.Timer: gebaseerd op terugbelfuncties, geschikt voor integratie met oudere code- Wikkel het werk bij elke tik in try/catch, zodat fouten de timer niet stoppen
- Voeg willekeurige spreiding toe voor gedistribueerde instanties om stormloop te voorkomen
- Gebruik
SemaphoreSlimom een tik over te slaan als de vorige nog wordt uitgevoerd - Bereken de aanvankelijke vertraging om de tikken op klokgrenzen uit te lijnen
Leer C# met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 93
- Lessen
- 346
Veelgestelde vragen
Is de les “Periodieke taken en timers” gratis?
Ja — de volledige tekst van “Periodieke taken en timers” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus C# Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus C# Academy bevat in totaal 4 lessen.
Wat leer ik in “Periodieke taken en timers”?
Plan terugkerend werk met PeriodicTimer en System.Threading.Timer en beheer annulering op een nette manier. Je oefent met C# Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met C# Academy te beginnen?
Ervaring vooraf is niet nodig. C# Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.
Hoe lang duurt de les “Periodieke taken en timers”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over C# Academy?
Ja. Elke les over C# Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- IHostedService en BackgroundService
- Worker Service-projecten
- Periodieke taken en timers
- Geplande taken met Quartz.NET