I've written a .NET Core console application to monitor the operation of a sensor network and I'd like a review of the task scheduling performed by the main program loop. There are three tasks that are all declared as public static async Task that perform the following operations:
CheckGateways.CheckAllconnects to an external API at one minute intervals and is the most likely to fail or take a long time. Normally it'll take a few seconds but I'd like other tasks to continue meanwhile.CheckNodes.CheckAllis an internal database check that may take a few seconds but is not time critical and runs once per minute.CheckAlerts.CheckAllchecks for alert conditions on the sensors and is the most time critical so I'm checking that once per second.
While the code seems to be working it hasn't been well stress-tested and a few things I'd like reviewed are:
Are there any potential race conditions with the way I'm checking the status of the first two tasks?
I'm repeating the code to start / check the first two tasks so perhaps there's a cleaner way to do that without introduction too much extra code as there's unlikely to be any more tasks added.
Also any general comments on coding / naming standards would be appreciated.
static async Task Main() { IConfiguration configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Build(); Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(configuration) .Enrich.FromLogContext() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .CreateLogger(); Task checkGateways = null; DateTime lastGatewayCheck = DateTime.UtcNow; Task checkNodes = null; DateTime lastNodeCheck = DateTime.UtcNow; while (true) { try { // Check gateway connectivity loss at one minute intervals if (DateTime.UtcNow.Subtract(lastGatewayCheck).TotalMinutes >= 1 && (checkGateways == null || checkGateways.IsCompleted)) { lastGatewayCheck = DateTime.UtcNow; checkGateways = CheckGateways.CheckAll(configuration); } if (checkGateways?.Status == TaskStatus.Faulted) { throw checkGateways.Exception; } // Check node connectivity loss at one minute intervals if (DateTime.UtcNow.Subtract(lastNodeCheck).TotalMinutes >= 1 && (checkNodes == null || checkNodes.IsCompleted)) { lastNodeCheck = DateTime.UtcNow; checkNodes = CheckNodes.CheckAll(configuration); } if (checkNodes?.Status == TaskStatus.Faulted) { throw checkNodes.Exception; } // Check for pending alerts to send at one second interval await CheckAlerts.CheckAll(configuration); } catch (AggregateException ae) { foreach (var ex in ae.InnerExceptions) { LogException(ex); } } catch (Exception ex) { LogException(ex); } await Task.Delay(1000); } } public static void LogException(Exception ex) { ConsoleMessage(ex.ToString()); Serilog.Log.Error(ex, "Error occured in TelemetryService"); } public static void ConsoleMessage(string msg) { Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss}: {msg}"); }