Created
July 17, 2017 09:31
-
-
Save davidfowl/a7dd5064d9dcf35b6eae1a7953d615e3 to your computer and use it in GitHub Desktop.
A base class that allows writing a long running background task in ASP.NET Core 2.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| using Microsoft.Extensions.Hosting; | |
| namespace WebApplication24 | |
| { | |
| public abstract class HostedService : IHostedService | |
| { | |
| private Task _executingTask; | |
| private CancellationTokenSource _cts; | |
| public Task StartAsync(CancellationToken cancellationToken) | |
| { | |
| // Create a linked token so we can trigger cancellation outside of this token's cancellation | |
| _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); | |
| // Store the task we're executing | |
| _executingTask = ExecuteAsync(_cts.Token); | |
| // If the task is completed then return it | |
| if (_executingTask.IsCompleted) | |
| { | |
| return _executingTask; | |
| } | |
| // Otherwise it's running | |
| return Task.CompletedTask; | |
| } | |
| public async Task StopAsync(CancellationToken cancellationToken) | |
| { | |
| // Stop called without start | |
| if (_executingTask == null) | |
| { | |
| return; | |
| } | |
| // Signal cancellation to the executing method | |
| _cts.Cancel(); | |
| // Wait until the task completes or the stop token triggers | |
| await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken)); | |
| // Throw if cancellation triggered | |
| cancellationToken.ThrowIfCancellationRequested(); | |
| } | |
| // Derived classes should override this and execute a long running method until | |
| // cancellation is requested | |
| protected abstract Task ExecuteAsync(CancellationToken cancellationToken); | |
| } | |
| } |
Author
@yaireclipse YEP!
Is cancellationToken.ThrowIfCancellationRequested(); in StopAsync necessary?
HostedServiceExecutor doesn't catch OperationCancelledException and we get a lot of errors in console
Application started. Press Ctrl+C to shut down.
Application is shutting down...
crit: Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor[10]
An error occurred stopping the application
System.AggregateException: One or more errors occurred. (The operation was canceled.) (The operation was canceled.) (The operation was canceled.) ---> System.OperationCanceledException: The operation was canceled.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Oh my, I think I got it. The return
TaskbyStartAsyncis the task of starting the internal_executingTask. If_executingTask.IsCompletedso fast, then it's probably due to a failure, and anyway it could be considered as if the task of starting_executingTaskfailed, and the reason for the failure might be encapsulated withing_executingTask. That's why when_executingTask.IsCompleted,_executingTaskis returned. Otherwise, if_executingTaskis still running, then the task of starting_executingTaskhas completed successfully and thereforeTask.ComletedTaskis returned...This think this might also answer my second question. Since
StartAsyncdoesn't really create aTaskfor starting_executingTask, it returns different instances ofTask, depending on whether starting_executingTasksucceeded or not. If it succeeded - someTask.ComletedTaskis returned; otherwise, the internal_executingTaskis returned, which is a bit of a trick to propagate the internal error that occurred in the attempt of starting_executingTask. This forces to not use theasyncmodifier onStartAsync, as usingasyncwould inevitably return the firstawaitedTask, which would prevent using that trick.When you get around to it, I'd love to know if I understood correctly.
Thanks!
Yair