// NB: Async void should be avoided; it should only be used for event handlers.Timer.Elapsed is an event handler.So, it's not necessarily wrong here. // c.f. http://stackoverflow.com/questions/25007670/using-async-await-inside-the-timer-elapsed-event-handler-within-a-windows-servic private async void DoWorkAsync(object group) { // DoWork needs to be re-entrant as Timer may call the callback before the previous callback has returned. // So, because a task may take longer than the period itself, DoWork needs to check if it's still running. ShellContext shellContext = _orchardHost.GetOrCreateShellContext(_shellSettings); var groupName = group as string ?? ""; foreach (var task in _tasks[groupName]) { var taskName = task.GetType().FullName; using (var scope = shellContext.EnterServiceScope()) { try { if (_states[task] != BackgroundTaskState.Idle) { return; } lock (_states) { // Ensure Terminate() was not called before if (_states[task] != BackgroundTaskState.Idle) { return; } _states[task] = BackgroundTaskState.Running; } if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation("Start processing background task \"{0}\".", taskName); } await task.DoWorkAsync(scope.ServiceProvider, _applicationLifetime.ApplicationStopping); if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation("Finished processing background task \"{0}\".", taskName); } } catch (Exception ex) { if (Logger.IsEnabled(LogLevel.Error)) { Logger.LogError(ex, $"Error while processing background task \"{taskName}\""); } } finally { lock (_states) { // Ensure Terminate() was not called during the task if (_states[task] != BackgroundTaskState.Stopped) { _states[task] = BackgroundTaskState.Idle; } } } } } }