Skip to content

Instantly share code, notes, and snippets.

@solrevdev
Last active January 30, 2020 13:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save solrevdev/f28ab81363e9875d25b3f3453339a556 to your computer and use it in GitHub Desktop.
Save solrevdev/f28ab81363e9875d25b3f3453339a556 to your computer and use it in GitHub Desktop.
.NET Timer with CancellationToken support
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace tempy
{
public static class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) => services.AddHostedService<Worker>());
}
}
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UserSecretsId>dotnet-tempy-2E552B51-4FEB-4B21-9AFC-4E4C7A69F645</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.1" />
</ItemGroup>
</Project>
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace tempy
{
/// <summary>
/// Thanks to this post https://stackoverflow.com/a/56666084/2041
/// </summary>
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private Timer _timer;
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
public override Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Worker started at: {time}", DateTimeOffset.Now);
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_timer?.Change(Timeout.Infinite, 0);
if (_executingTask == null)
{
await Task.CompletedTask.ConfigureAwait(false);
}
try
{
_logger.LogInformation("Worker stopping at: {time}", DateTimeOffset.Now);
_stoppingCts.Cancel();
}
finally
{
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, stoppingToken)).ConfigureAwait(false);
}
await Task.CompletedTask.ConfigureAwait(false);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.CompletedTask.ConfigureAwait(false);
}
public override void Dispose()
{
_timer?.Dispose();
base.Dispose();
}
private void DoWork(object state)
{
GC.KeepAlive(_timer);
_timer?.Change(Timeout.Infinite, 0);
_executingTask = DoWorkAsync(_stoppingCts.Token);
}
private async Task DoWorkAsync(CancellationToken stoppingToken)
{
if (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker starting DoWorkAsync at: {time}", DateTimeOffset.Now);
Thread.Sleep(5000);
_logger.LogInformation("Worker completed DoWorkAsync at: {time}", DateTimeOffset.Now);
_timer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(-1));
await Task.CompletedTask.ConfigureAwait(false);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment