How do I run an async task in a Kestrel process with a very long time interval (say daily or perhaps even longer)? The task needs to run in the memory space of the web server process to update some global variables that slowly go out of date.
Bad answers:
Trying to use an OS scheduler is a poor plan.
Calling await from a controller is not acceptable. The task is slow.
The delay is too long for Task.Delay() (about 16 hours or so and Task.Delay will throw).
HangFire, etc. make no sense here. It's an in-memory job that doesn't care about anything in the database. Also, we can't call the database without a user context (from a logged-in user hitting some controller) anyway.
System.Threading.Timer. It's reentrant.
Bonus:
The task is idempotent. Old runs are completely irrelevant.
It doesn't matter if a particular page render misses the change; the next one will get it soon enough.
As this is a Kestrel server we're not really worried about stopping the background task. It'll stop when the server process goes down anyway.
The task should run once immediately on startup. This should make coordination easier.
Some people are missing this. The method is async. If it wasn't async the problem wouldn't be difficult.
I am going to add an answer to this, because this is the only logical way to accomplish such a thing in ASP.NET Core: an IHostedService implementation.
This is a non-reentrant timer background service that implements IHostedService.
public sealed class MyTimedBackgroundService : IHostedService
{
private const int TimerInterval = 5000; // change this to 24*60*60 to fire off every 24 hours
private Timer _t;
public async Task StartAsync(CancellationToken cancellationToken)
{
// Requirement: "fire" timer method immediatly.
await OnTimerFiredAsync();
// set up a timer to be non-reentrant, fire in 5 seconds
_t = new Timer(async _ => await OnTimerFiredAsync(),
null, TimerInterval, Timeout.Infinite);
}
public Task StopAsync(CancellationToken cancellationToken)
{
_t?.Dispose();
return Task.CompletedTask;
}
private async Task OnTimerFiredAsync()
{
try
{
// do your work here
Debug.WriteLine($"{TimerInterval / 1000} second tick. Simulating heavy I/O bound work");
await Task.Delay(2000);
}
finally
{
// set timer to fire off again
_t?.Change(TimerInterval, Timeout.Infinite);
}
}
}
So, I know we discussed this in comments, but System.Threading.Timer callback method is considered a Event Handler. It is perfectly acceptable to use async void in this case since an exception escaping the method will be raised on a thread pool thread, just the same as if the method was synchronous. You probably should throw a catch in there anyway to log any exceptions.
You brought up timers not being safe at some interval boundary. I looked high and low for that information and could not find it. I have used timers on 24 hour intervals, 2 day intervals, 2 week intervals... I have never had them fail. I have a lot of them running in ASP.NET Core in production servers for years, too. We would have seen it happen by now.
OK, so you still don't trust System.Threading.Timer...
Let's say that, no... There is just no fricken way you are going to use a timer. OK, that's fine... Let's go another route. Let's move from IHostedService to BackgroundService (which is an implementation of IHostedService) and simply count down.
This will alleviate any fears of the timer boundary, and you don't have to worry about async void event handlers. This is also a non-reentrant for free.
public sealed class MyTimedBackgroundService : BackgroundService
{
private const long TimerIntervalSeconds = 5; // change this to 24*60 to fire off every 24 hours
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Requirement: "fire" timer method immediatly.
await OnTimerFiredAsync(stoppingToken);
var countdown = TimerIntervalSeconds;
while (!stoppingToken.IsCancellationRequested)
{
if (countdown-- <= 0)
{
try
{
await OnTimerFiredAsync(stoppingToken);
}
catch(Exception ex)
{
// TODO: log exception
}
finally
{
countdown = TimerIntervalSeconds;
}
}
await Task.Delay(1000, stoppingToken);
}
}
private async Task OnTimerFiredAsync(CancellationToken stoppingToken)
{
// do your work here
Debug.WriteLine($"{TimerIntervalSeconds} second tick. Simulating heavy I/O bound work");
await Task.Delay(2000);
}
}
A bonus side-effect is you can use long as your interval, allowing you more than 25 days for the event to fire as opposed to Timer which is capped at 25 days.
You would inject either of these as so:
services.AddHostedService<MyTimedBackgroundService>();
Related
I don't quite understand the difference between Task.Wait and await.
I have something similar to the following functions in a ASP.NET WebAPI service:
public class TestController : ApiController
{
public static async Task<string> Foo()
{
await Task.Delay(1).ConfigureAwait(false);
return "";
}
public async static Task<string> Bar()
{
return await Foo();
}
public async static Task<string> Ros()
{
return await Bar();
}
// GET api/test
public IEnumerable<string> Get()
{
Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());
return new string[] { "value1", "value2" }; // This will never execute
}
}
Where Get will deadlock.
What could cause this? Why doesn't this cause a problem when I use a blocking wait rather than await Task.Delay?
Wait and await - while similar conceptually - are actually completely different.
Wait will synchronously block until the task completes. So the current thread is literally blocked waiting for the task to complete. As a general rule, you should use "async all the way down"; that is, don't block on async code. On my blog, I go into the details of how blocking in asynchronous code causes deadlock.
await will asynchronously wait until the task completes. This means the current method is "paused" (its state is captured) and the method returns an incomplete task to its caller. Later, when the await expression completes, the remainder of the method is scheduled as a continuation.
You also mentioned a "cooperative block", by which I assume you mean a task that you're Waiting on may execute on the waiting thread. There are situations where this can happen, but it's an optimization. There are many situations where it can't happen, like if the task is for another scheduler, or if it's already started or if it's a non-code task (such as in your code example: Wait cannot execute the Delay task inline because there's no code for it).
You may find my async / await intro helpful.
Based on what I read from different sources:
An await expression does not block the thread on which it is executing. Instead, it causes the compiler to sign up the rest of the async method as a continuation on the awaited task. Control then returns to the caller of the async method. When the task completes, it invokes its continuation, and execution of the async method resumes where it left off.
To wait for a single task to complete, you can call its Task.Wait method. A call to the Wait method blocks the calling thread until the single class instance has completed execution. The parameterless Wait() method is used to wait unconditionally until a task completes. The task simulates work by calling the Thread.Sleep method to sleep for two seconds.
This article is also a good read.
Some important facts were not given in other answers:
async/await is more complex at CIL level and thus costs memory and CPU time.
Any task can be canceled if the waiting time is unacceptable.
In the case of async/await we do not have a handler for such a task to cancel it or monitoring it.
Using Task is more flexible than async/await.
Any sync functionality can by wrapped by async.
public async Task<ActionResult> DoAsync(long id)
{
return await Task.Run(() => { return DoSync(id); } );
}
async/await generate many problems. We do not know if await statement will be reached without runtime and context debugging. If first await is not reached, everything is blocked. Sometimes even when await seems to be reached, still everything is blocked:
https://github.com/dotnet/runtime/issues/36063
I do not see why I must live with the code duplication for sync and async method or using hacks.
Conclusion: Creating Tasks manually and controlling them is much better. Handler to Task gives more control. We can monitor Tasks and manage them:
https://github.com/lsmolinski/MonitoredQueueBackgroundWorkItem
Sorry for my english.
Based on the heartbeat sample project, my understanding is that if you want to schedule an activity on an interval (e.g. every 10 minutes) then you have to consume and produce a new state with the next scheduled activity each time. This seems unnecessary if nothing is changing except the next scheduled activity. Is there a way to have interval activities without consuming the state?
The purpose of the SchedulableState is to have an event scheduled on the state at a particular time. For example- an auction going inactive when a deadline is reached, such that it stops receiving bids. SchedulableState is particularly useful in these kinds of use cases.
SchedulableState schedulable an event, the event triggers a flow. The flow mostly runs a transaction which will in mosyt cases (unless you are doing an issuance) consume a state.
However, if you want to schedule something which out a SchedulableState using a Service as below:
#CordaService
public class SchedulerService extends SingletonSerializeAsToken {
private AppServiceHub serviceHub;
public SchedulerService(AppServiceHub serviceHub) {
this.serviceHub = serviceHub;
schedule();
}
private void schedule(){
Timer timer = new Timer();
TimerTask task = new Helper();
timer.schedule(task, 2000, 5000);
}
static class Helper extends TimerTask
{
public static int i = 0;
public void run()
{
System.out.println("Timer ran " + ++i);
}
}
}
I got some operations in my Controller class which could take some time. So I want to show a loading dialog while this operation is running.
I tried this:
Platform.runLater(new Runnable() {
#Override
public void run() {
loadingDialog.show();
}
});
Boolean opSuccess = myService.operate();
Platform.runLater(new Runnable() {
#Override
public void run() {
loadingDialog.hide();
}
});
if (opSuccess) {
// continue
}
Now, the Problem is, the loadingDialog is never show. The UI only blocks for some time and than continues on "//continue".
So it seems, the runLater call is blocked by the blocking operation (operate)?
I also tried CoundDownLatch, to wait for loadingDialog.show() to complete, before running myService.operate(). But the latch.await() method never completes.
So my question is, how my I show the loadingDialog until myService.operate() finished and returned true or false? Do I have to put the operate() call into another thread and run it async or is there an easier way?
Thanks for help.
Are you sure your entire code does not run in the JavaFX Thread?
Methods of your controller class usually do and I assume it due to your description.
However, better use the Task class. Here you'll find a tutorial and a short snippet for your application:
// here runs the JavaFX thread
// Boolean as generic parameter since you want to return it
Task<Boolean> task = new Task<Boolean>() {
#Override public Boolean call() {
// do your operation in here
return myService.operate();
}
};
task.setOnRunning((e) -> loadingDialog.show());
task.setOnSucceeded((e) -> {
loadingDialog.hide();
Boolean returnValue = task.get();
// process return value again in JavaFX thread
});
task.setOnFailed((e) -> {
// eventual error handling by catching exceptions from task.get()
});
new Thread(task).start();
I assumed Java 8 and the possibility to use Lambda expressions. Of course it is possible without them.
You are better off making use of concurrency mechanisms/Worker interfaces in JavaFx - Tasks and services instead of using Platform.runLater(). The tasks and services allow you to manage the long running tasks in a separate thread. They also provide callbacks to indicate the progress of the tasks.
You could explore further at http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm
Also have a look at the Ensemble JavaFX samples for Tasks and Services - http://www.oracle.com/technetwork/java/javase/overview/javafx-samples-2158687.html
In the book Pro ASP.NET MVC 4 there is an example of an asynchronous action:
public class RemoteDataController : AsyncController
{
public async Task<ActionResult> ConsumeAsyncMethod() {
string data = await new RemoteService().GetRemoteDataAsync();
return View("Data", (object)data);
}
}
public class RemoteService
{
public async Task<string> GetRemoteDataAsync() {
return await Task<string>.Factory.StartNew(() => {
Thread.Sleep(2000);
return "Hello from the other side of the world";
});
}
}
My question is: Would the task not just use a thread from the threadpool that is also used for serving requests?
Say I have a synchronous I/O bound method. I think calling this method with Task.Run and await in my action wouldn't lead to more requests that can be handled concurrently because the task for the I/O bound method is not available any longer for request handling. Or is there a separate threadpool only for the requests and using Task.Run in actions automatically uses a different one? What got me thinking is this question: Using ThreadPool.QueueUserWorkItem in ASP.NET in a high traffic scenario where the answer was more or less that only async methods from libraries should be used, where those libraries use their own thread pool.
Is it possible to configure the behavior? Does it work the same way with ASP.NET WebForms?
example
That's a really poor example. There are three things that I see immediately wrong with it, but the major one is as you pointed out:
Would the task not just use a thread from the threadpool that is also used for serving requests?
Yes, that example would.
Please consider this example instead:
public class RemoteDataController : Controller
{
public async Task<ActionResult> ConsumeAsyncMethod() {
string data = await new RemoteService().GetRemoteDataAsync();
return View("Data", data);
}
}
public class RemoteService
{
public async Task<string> GetRemoteDataAsync() {
await Task.Delay(2000);
return "Hello from the other side of the world";
}
}
The original example blocked a thread pool thread using Thread.Sleep. That's completely counterproductive on ASP.NET. As a general rule, do not use Task.Factory.StartNew or Task.Run on ASP.NET.
In contrast, Task.Delay is a naturally-asynchronous operation. By "naturally-asynchronous", I mean asynchronous in the same way that I/O operations are asynchronous (e.g., HttpClient for web calls). Naturally-asynchronous operations do not use threads, hence their appeal for ASP.NET servers (reducing pressure on the thread pool, allowing you to scale more).
It's interesting to think about how this works: when you use naturally-asynchronous methods as in my example, a thread starts the request up until it hits the await; at that point the request thread is returned to the thread pool (!) and for the next two seconds there are no threads processing that request (and yet the request has not completed). I like to call this phenomenon "zero-threaded concurrency". When the Delay finishes, a thread resumes processing the request and completes it.
On a side note, AsyncController is a leftover from MVC3. It is not needed with async/await.
In my Singleton-EJB i start a TimerService every 2 minutes. When a client access the test method
sometimes the application runs into a deadlock. The problem is, the test method calls a asynchronous method inside the EJB (see Method determineABC). The deadlock happens when the scheduleMethod tries to create a single action timer and therefore tries to acquire a lock (because hte timer callback method is annotated with LOCK.WRITE). At the same time we are already in the determineABC Method which tries to invoke the asynchronous method asynchMethod. Maybe the call of ejbLocal.asynchMethod(...); also tries to acquire a lock. Anyway here i run into a deadlock, because the asynchronous method is never called. So what is the problem?
Here is a source code snippet:
#Singleton
#Startup
#TransactionManagement(TransactionManagementType.CONTAINER)
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
#ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class XEJB implements XEJBLocal {
#javax.annotation.Resource(name = "x/XEJB/TimeService")
private TimerService timerService;
#javax.annotation.Resource
private SessionContext ctx;
#Schedule(minute = "*/2", hour = "*", persistent = false)
#Lock(LockType.READ)
private void scheduleMethod() {
// Create Single Action Timer
timerService.createSingleActionTimer(new Date(), new TimerConfig(null, false));
}
#Timeout
#Lock(LockType.WRITE)
private void timer(Timer timer) {
// Do something
}
#Override
#Lock(LockType.READ)
public B test(...) {
return determineABC(...);
}
#Lock(LockType.READ)
private B determineABC(...) {
XEJBLocal ejb= (XEJBLocal) ctx.getBusinessObject(ctx.getInvokedBusinessInterface());
Future<ArrayList> result = null;
result = ejb.asynchMethod(...);
result.get(4, TimeUnit.MINUTES); // Sometimes runs into a DEADLOCK
...
}
#Asynchronous
#Override
#Lock(LockType.READ)
public Future<ArrayList> asynchMethod(...) {
...
return new AsyncResult<ArrayList>(abcList);
}
The Deadlock also happens when i only use the #Schedule Method and no TimerService...
The DeadLock also happens when i do not use a Future Object but void as return type of the asynchronous Method.
When the timeout Exception is thrown the deadlock is solved. When i annotate the timer method with #AccessTimeout(2000) and this time is up the asynchronous method is called and therefore the deadlock is also solved.
When i use Locktype.READ for the timer Method no Deadlock happens. But why? What does the asychronous method call?
READ locks have to wait for WRITE locks to finish before they start their work. When timer() is working all your other invokations, even to READ methods, are going to wait. Are you sure the timeout happens in result.get(4, TimeUnit.MINUTES);?
I think you may be have access timeouts in test() invokation, way before reaching result.get(4, TimeUnit.MINUTES);.