I have a process I would like to run in the background. This is executed with a click of an action link.
Action to call:
public async Task<ActionResult> ProcessRec()
{
await Task.Run(() => waitTimer());
return RedirectToAction("Index", "Home");
}
public void waitTimer()
{
Thread.Sleep(10000);
}
This however waits for the full 10 seconds before redirecting me to my "Index, Home" action. I am very new to Await/Async so I know I am interpreting something wrong here. How do I get the application to return to this action, while the waitTimer is executing in the background? Thanks!!
await, as you found out, blocks the response from returning to the user before it is done. Normally you would just put your background work on another thread and set it to "fire and forget" by not awaiting, however in ASP.NET IIS will shut down AppDomains that are not being used and Task.Run does not inform IIS that your background thread "is using the AppDomain" so your background thread could be terminated with a Thread.Abort() during an AppDomain shutdown.
If you are using .NET 4.5.2 or newer you can tell IIS you have a background worker that you need to be kept alive via QueueBackgroundWorkItem. You would use it like this
public ActionResult ProcessRec()
{
HostingEnvironment.QueueBackgroundWorkItem(waitTimer);
return RedirectToAction("Index", "Home");
}
public void waitTimer(CancellationToken token)
{
Thread.Sleep(10000);
}
//You also could do
public async Task waitTimer2(CancellationToken token)
{
await Task.Delay(10000);
}
Now this does not guarantee that IIS will not shut down your app domain but it does let it know you are in the middle of something and asks for more time when it does try to shut it down (You get up to 90 additional seconds after a shutdown is started to complete all queued background items by default).
For more information read this MSDN blog introducing it.
This however waits for the full 10 seconds before redirecting me to my "Index, Home" action.
Right, that's because await asynchronously waits for the operations completion. It will yield the thread back to the pool until the operation completes.
How do I get the application to return to this action, while the waitTimer is executing in the background?
Task.Run is dangerous in the fact it doesn't register work with IIS which can lead to problems. Instead, you can use BackgroundTaskManager or HangFire which register it's execution with ASP.NET:
BackgroundTaskManager.Run(() => { waitTimer() };
return RedirectToAction("Index", "Home");
I'm thinking about sending a message to a queue (like azure storage/service bus queue) so that you can get your response immediately.
And then create another service to dequeue and process the message (execute your long running task)
Also if this is an azure website (web app), you can use the web job!
Hope that helps.
public async Task<IActionResult> Index()
{
return await Task.Run(() =>
{
return View();
});
}
Related
In asp .net MVC project I'm trying to call controller's Get method with some long lasting operation asynchronously. The problem is that after first call other incoming requests are waiting when this operation will be finished. And after that they are handled one by one in order they were requested.
Tested on code below.
So if for example I make requests to SomeGetMethod multiple times, for the incoming request this method will be executed only when SomeGetMethod finished for previous call.
public class SomeController : Controller
{
public ActionResult Index()
{
return View();
}
public async Task<ActionResult> SomeGetMethod(int id)
{
await TestAsync();
return Content("1");
}
public async Task<int> TestAsync()
{
await Task.Run(() => Thread.Sleep(20000));
return 1;
}
}
What might be the reason of that?
Thanks in advance!
Task.Run could be executed on the same thread as your controller, so you should change Thread.Sleep with Task.Delay.
No that not the case. Its been pretending like this because both of the API's have same level of thread.sleep so its obvious that it will perform FIFO behavior i.e API hits first will response first
To verify this kindly set Thread.Sleep(1) for the second API during debugging and you will find out that second API will response before the response of first API.
You can also verify by placing a debugger and you will find out that second API will hit server as soon as you send request.
You are experiencing Threadpool starvation. You start off with relatively few threads in your pool that can service requests. What you are doing is blocking a threadpool thread for 20 seconds, which means it is doing nothing for that period. When you hit that endpoint over and over, you will run into a situation where all your threads that can service requests are tied up, doing nothing but waiting.
To fix this, don't offload to a threadpool thread i.e. don't do Task.Run() and change to await Task.Delay(20000) for optimal throughput.
You should do a more correct test, so only one of the methods is waiting.
public class SomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult NoWait()
{
return Content("1");
}
public async Task<int> SleepWait()
{
await Task.Run(() => Thread.Sleep(20000));
return 2;
}
}
Then call SleepWait() and right after NoWait(), what is the output then?
I want to call a method inside some method and do not want the process to wait or take time on other method completion.
like this
public ActionResult Insert(int userId)
{
_userService.Insert(userId);
SyncUserInSomeOtherCollection(userId);
return new EmptyResult();
}
private SyncUserInSomeOtherCollection(int userId)
{
//Do Sync work which will actually take some time
}
I want to make SyncUserInSomeOtherCollection() work in such a way so that the main method return result without any wait.
I tried to run a task like this
Task.Run(async () => await SyncUserInSomeOtherCollection(userId)).Result;
But not sure if this a good approach to follow.
Based on the code that you shared, I assume you're working on ASP.NET. I've come across same scenario and found QueueBackgroundWorkItem to work for me, it also allows you to have graceful shutdown of your long running task when Worker Process is stopped / shutdown.
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
{
if (!cancellationToken.IsCancellationRequested)
{
//Do Action
}
else
{
//Handle Graceful Shutdown
}
}
There are more ways to accomplish this. I suggest you go through this excellent article by Scott Hanselman.
I have a process I would like to run in the background. This is executed with a click of an action link.
Action to call:
public async Task<ActionResult> ProcessRec()
{
await Task.Run(() => waitTimer());
return RedirectToAction("Index", "Home");
}
public void waitTimer()
{
Thread.Sleep(10000);
}
This however waits for the full 10 seconds before redirecting me to my "Index, Home" action. I am very new to Await/Async so I know I am interpreting something wrong here. How do I get the application to return to this action, while the waitTimer is executing in the background? Thanks!!
await, as you found out, blocks the response from returning to the user before it is done. Normally you would just put your background work on another thread and set it to "fire and forget" by not awaiting, however in ASP.NET IIS will shut down AppDomains that are not being used and Task.Run does not inform IIS that your background thread "is using the AppDomain" so your background thread could be terminated with a Thread.Abort() during an AppDomain shutdown.
If you are using .NET 4.5.2 or newer you can tell IIS you have a background worker that you need to be kept alive via QueueBackgroundWorkItem. You would use it like this
public ActionResult ProcessRec()
{
HostingEnvironment.QueueBackgroundWorkItem(waitTimer);
return RedirectToAction("Index", "Home");
}
public void waitTimer(CancellationToken token)
{
Thread.Sleep(10000);
}
//You also could do
public async Task waitTimer2(CancellationToken token)
{
await Task.Delay(10000);
}
Now this does not guarantee that IIS will not shut down your app domain but it does let it know you are in the middle of something and asks for more time when it does try to shut it down (You get up to 90 additional seconds after a shutdown is started to complete all queued background items by default).
For more information read this MSDN blog introducing it.
This however waits for the full 10 seconds before redirecting me to my "Index, Home" action.
Right, that's because await asynchronously waits for the operations completion. It will yield the thread back to the pool until the operation completes.
How do I get the application to return to this action, while the waitTimer is executing in the background?
Task.Run is dangerous in the fact it doesn't register work with IIS which can lead to problems. Instead, you can use BackgroundTaskManager or HangFire which register it's execution with ASP.NET:
BackgroundTaskManager.Run(() => { waitTimer() };
return RedirectToAction("Index", "Home");
I'm thinking about sending a message to a queue (like azure storage/service bus queue) so that you can get your response immediately.
And then create another service to dequeue and process the message (execute your long running task)
Also if this is an azure website (web app), you can use the web job!
Hope that helps.
public async Task<IActionResult> Index()
{
return await Task.Run(() =>
{
return View();
});
}
I have the following methods:
public override bool SyncNavigationMethod()
{
AsyncShowDialog().Wait();
return true;
}
public Task AsyncShowDialog()
{
//code to show dialog and process user input from the dialog.
}
what happens is that the app goes into a deadlock when SyncNavigationMethod is called.
My guess is that it runs on the MainThread, who is waiting for AsyncShowDialog to complete, and the dialog that is shown cannot call it's callback because it's the MainThread that calls it. And so, the main thread waits to itself and it's a deadlock.
More information:
runtime - UWP.
device: phone (as described above) desktop (the app freezes before the dialog is shown).
I have tried to use .Wait() \ .Result on AsyncShowDialog.
I have tried to use a ManualResetEvent after calling the AsyncShowDialog to block the code from progressing while AsyncShowDialog is running.
Am I right suspecting a deadLock?
How can I call AsyncShowDialog from SyncNavigationMethod without this deadlock?
Thanks.
I will start with one of the best blog post by Stephen Cleary, which explains, how the synchronous blocking of an asynchronous call leads to deadlock and that's true for all systems except Console, which doesn't have a UI context thread:
Now regarding your code and points:
And so, the main thread waits to itself and it's a deadlock.
Yes it does, you have correctly understood it's a deadlock, due to Mainthread blocking itself
Wait, Result and ManualResetEvent, they all synchronously block the Async code, thus the same result
Possible solution:
If the call is a Rest API or a web based call, then make the everything Async and IIS will ensure that it can return post top level Async-Await execution, something like:
public async override Task<bool> SyncNavigationMethod()
{
await AsyncShowDialog();
return true;
}
public async Task AsyncShowDialog()
{
await SomeOperation();
}
Return value of Task<bool> would be automatically unwrapped
Other option:
If you cannot make the SyncNavigationMethod() Async, then try this:
public override bool SyncNavigationMethod()
{
List<Task> taskList = new List<Task>();
taskList.Add(AsyncShowDialog());
Task.WhenAll(taskList.ToArray());
return true;
}
public async Task AsyncShowDialog()
{
await SomeOperation();
}
I have an issue with this simple async code. The execution goes througth the TestAsync action and goes througth the delay method but when the delay method returns nothing else happens.
it seems like blocked for some reason.
public async Task<ActionResult> TestAsync()
{
try
{
var res = await doLongOperation();
return RedirectToAction("Index");
}
catch (Exception e) { }
}
private Task<bool> doLongOperation()
{
var test = new Task<bool>(() => { /*do the long operation..*/ return true; });
return test;
}
Any suggestion?
new Task<bool>(() => true) returns a task that is not started yet. This is an old syntax that requires you to invoke the Start method on the task. If you don't invoke the Start method, the task will never complete, and thus asynchronous execution will never continue (await will asynchronously wait forever).
If you simply want to delay execution, then simply use Task.Delay like this:
private async Task<bool> delay()
{
await Task.Delay(1000);
return true;
}
In general, it is always recommended to create tasks that are already started upon creation.
If you want to create a task that runs on a thread-pool thread, then use the Task.Run method.
The method posted in your question doesn't work properly because the task is not started, there's nothing finishing so your await is waiting indefinitely. If you want to use Async it's best to use async all the way down, especially in an ASP.NET context. If you are not doing anything that is truly asynchronous, then in a web development context, just allow it to be synchronous.
I honestly believe the most good that will be done here is for you to spend time studying async/await, an excellent resource is found here: Stephen Cleary Blog on Async