Running awaitable task asynchronously in asp.net - c#

I have the following action in my controller:
[HttpPost("run")]
public Task<object> Run([FromBody] ResearchRequest researchRequest)
{
researchService.RunAsync(researchRequest);
return new{ queued = true };
}
The controller needs to handle a task that takes several minutes.
Is this the correct way to release researchService.RunAsync to handle its job?
Or is there a better approach.
Thanks

If you are wanting to check that a process is already running, you could mark it as in progress somewhere on the server side (in a task in the database or such) and then when displaying the UI call a method on the server to check the state of your in progress flag.
that way the UI could navigate away from that page and return to still see that the process had been started

You can do that if RunAsync is making I/O requests, but you wouldn't return a Task:
[HttpPost("run")]
public object Run([FromBody] ResearchRequest researchRequest)
{
researchService.RunAsync(researchRequest);
return new{ queued = true };
}
That will start running RunAsync on the same thread, just like any other method. At the first await in RunAsync that acts on an incomplete Task, RunAsync will return its own incomplete Task, at which point control returns back to your Run action and your object is returned. You won't be waiting for whatever I/O operation RunAsync makes.
If RunAsync is taking a long time because of CPU calculations (not I/O), then that won't do anything for you because, remember, it starts running on the same thread. You will have to start it on another thread, which you can do using Task.Run:
[HttpPost("run")]
public Task<object> Run([FromBody] ResearchRequest researchRequest)
{
Task.Run(() => researchService.RunAsync(researchRequest));
return new{ queued = true };
}
But!
In both cases, ASP.NET will have no idea that RunAsync is running in the background. If the IIS app pool is shut down or recycled for any reason, that job will be killed part way through. Note that by default, IIS is configured to shut down an app pool after 20 minutes of no HTTP requests coming in.
If that is unacceptable to you, then you're better off writing the job to a queue in a database or something and doing that background processing in a Windows service.

Related

Does QueueBackgroundWorkItem actually queue background work?

We're running an ASP.NET WebAPI 2 service and we want to log some requests with our logger to email/database.
Because it's background work, and because in asp.net I figured we should use HostingEnvironment.QueueBackgroundWorkItem to run it in the background.
I want my logs to all be in order - to my surprise I could not find anything indicating that QueueBackgroundWorkItem actually guarantees that the queued work items run in order or indicates it doesn't.
So, my question is: Does QueueBackgroundWorkItem guarantee that work queued gets executed in order?
HostingEnvironment.QueueBackgroundWorkItem((e) => Console.WriteLine("A"));
HostingEnvironment.QueueBackgroundWorkItem((e) => Console.WriteLine("B"));
Do I know that the output of the above snippet is always:
A
B
Or can it be out of order?
There appears to be nothing contractual in the documentation.
Looking at the reference source, it appears to use a class called BackgroundWorker to actually execute these tasks.
In turn, this appears to be running the tasks on the ThreadPool and explicitly may be executing multiple tasks in parallel:
public void ScheduleWorkItem(Func<CancellationToken, Task> workItem) {
Debug.Assert(workItem != null);
if (_cancellationTokenHelper.IsCancellationRequested) {
return; // we're not going to run this work item
}
// Unsafe* since we want to get rid of Principal and other constructs specific to the current ExecutionContext
ThreadPool.UnsafeQueueUserWorkItem(state => {
lock (this) {
if (_cancellationTokenHelper.IsCancellationRequested) {
return; // we're not going to run this work item
}
else {
_numExecutingWorkItems++;
}
}
RunWorkItemImpl((Func<CancellationToken, Task>)state);
}, workItem);
}
So I'd say it's unsafe to assume anything about what order two queued tasks will complete in.
QueueBackgroundWorkItem guarantee that work queued gets executed in order
Reference from HostingEnvironment.QueueBackgroundWorkItem
New HostingEnvironment.QueueBackgroundWorkItem method that lets you schedule small background work items. ASP.NET tracks these items and prevents IIS from abruptly terminating the worker process until all background work items have completed. These will enable ASP.NET applications to reliably schedule Async work items.

How can I have two separate task schedulers?

I am writing a game, and using OpenGL I require that some work be offloaded to the rendering thread where an OpenGL context is active, but everything else is handled by the normal thread pool.
Is there a way I can force a Task to be executed in a special thread-pool, and any new tasks created from an async also be dispatched to that thread pool?
I want a few specialized threads for rendering, and I would like to be able to use async and await for example for creating and filling a vertex buffer.
If I just use a custom task scheduler and a new Factory(new MyScheduler()) it seems that any subsequent Task objects will be dispatched to the thread pool anyway where Task.Factory.Scheduler suddenly is null.
The following code should show what I want to be able to do:
public async Task Initialize()
{
// The two following tasks should run on the rendering thread pool
// They cannot run synchronously because that will cause them to fail.
this.VertexBuffer = await CreateVertexBuffer();
this.IndexBuffer = await CreateIndexBuffer();
// This should be dispatched, or run synchrounousyly, on the normal thread pool
Vertex[] vertices = CreateVertices();
// Issue task for filling vertex buffer on rendering thread pool
var fillVertexBufferTask = FillVertexBufffer(vertices, this.VertexBuffer);
// This should be dispatched, or run synchrounousyly, on the normal thread pool
short[] indices = CreateIndices();
// Wait for tasks on the rendering thread pool to complete.
await FillIndexBuffer(indices, this.IndexBuffer);
await fillVertexBufferTask; // Wait for the rendering task to complete.
}
Is there any way to achieve this, or is it outside the scope of async/await?
This is possible and basically the same thing what Microsoft did for the Windows Forms and WPF Synchronization Context.
First Part - You are in the OpenGL thread, and want to put some work into the thread pool, and after this work is done you want back into the OpenGL thread.
I think the best way for you to go about this is to implement your own SynchronizationContext. This thing basically controls how the TaskScheduler works and how it schedules the task. The default implementation simply sends the tasks to the thread pool. What you need to do is to send the task to a dedicated thread (that holds the OpenGL context) and execute them one by one there.
The key of the implementation is to overwrite the Post and the Send methods. Both methods are expected to execute the callback, where Send has to wait for the call to finish and Post does not. The example implementation using the thread pool is that Sendsimply directly calls the callback and Post delegates the callback to the thread pool.
For the execution queue for your OpenGL thread I am think a Thread that queries a BlockingCollection should do nicely. Just send the callbacks to this queue. You may also need some callback in case your post method is called from the wrong thread and you need to wait for the task to finish.
But all in all this way should work. async/await ensures that the SynchronizationContext is restored after a async call that is executed in the thread pool for example. So you should be able to return to the OpenGL thread after you did put some work off into another thread.
Second Part - You are in another thread and want to send some work into the OpenGL thread and await the completion of that work.
This is possible too. My idea in this case is that you don't use Tasks but other awaitable objects. In general every object can be awaitable. It just has to implement a public method getAwaiter() that returns a object implementing the INotifyCompletion interface. What await does is that it puts the remaining method into a new Action and sends this action to the OnCompleted method of that interface. The awaiter is expected to call the scheduled actions once the operation it is awaiting is done. Also this awaiter has to ensure that the SynchronizationContext is captured and the continuations are executed on the captured SynchronizationContext. That sounds complicated, but once you get the hang of it, it goes fairly easy. What helped me a lot is the reference source of the YieldAwaiter (this is basically what happens if you use await Task.Yield()). This is not what you need, but I think it is a place to start.
The method that returns the awaiter has to take care of sending the actual work to the thread that has to execute it (you maybe already have the execution queue from the first part) and the awaiter has to trigger once that work is done.
Conclusion
Make no mistake. That is a lot of work. But if you do all that you will have less problem down the line because you can seamless use the async/await pattern as if you would be working inside windows forms or WPF and that is a hue plus.
First, realize that await introduces the special behavior after the method is called; that is to say, this code:
this.VertexBuffer = await CreateVertexBuffer();
is pretty much the same as this code:
var createVertexBufferTask = CreateVertexBuffer();
this.VertexBuffer = await createVertexBufferTask;
So, you'll have to explicitly schedule code to execute a method within a different context.
You mention using a MyScheduler but I don't see your code using it. Something like this should work:
this.factory = new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None, new MyScheduler());
public async Task Initialize()
{
// Since you mention OpenGL, I'm assuming this method is called on the UI thread.
// Run these methods on the rendering thread pool.
this.VertexBuffer = await this.factory.StartNew(() => CreateVertexBuffer()).Unwrap();
this.IndexBuffer = await this.factory.StartNew(() => CreateIndexBuffer()).Unwrap();
// Run these methods on the normal thread pool.
Vertex[] vertices = await Task.Run(() => CreateVertices());
var fillVertexBufferTask = Task.Run(() => FillVertexBufffer(vertices, this.VertexBuffer));
short[] indices = await Task.Run(() => CreateIndices());
await Task.Run(() => FillIndexBuffer(indices, this.IndexBuffer));
// Wait for the rendering task to complete.
await fillVertexBufferTask;
}
I would look into combining those multiple Task.Run calls, or (if Initialize is called on a normal thread pool thread) removing them completely.

ReadOnly Session coupled with Asynchronous Action and Task Thread. How does the Controller behave then?

In an Asp.Net MVC application if an Asynchronous Controller's Session behavior is Read only, its Action method is also Asynchronous and within it we create a Task Thread that does some long running stuff, example:
[SessionState(SessionStateBehavior.ReadOnly)]
public class UploadController : AsyncController
{
public void UploadFilesAsync(IEnumerable<HttpPostedFileBase> assetFiles,
string filesMetaInfo)
{
var postedFiles = assetFiles;
var fInfo = filesMetaInfo;
AsyncManager.OutstandingOperations.Increment();
Task.Factory.StartNew(
() => ProcessUploadedFile(postedFiles, fInfo),
CancellationToken.None, TaskCreationOptions.DenyChildAttach,
TaskScheduler.FromCurrentSynchronizationContext());
}
public ActionResult UploadFilesCompleted(object result)
{
return Json(new
{
status = "OK"
}, "text/plain");
}
private void ProcessUploadedFile(IEnumerable<HttpPostedFileBase>
assetFiles, string filesInfo)
{
// Do some long running stuff here like file processing.
// ......................
// ..................................
AsyncManager.Parameters["result"] = "success"
AsyncManager.OutstandingOperations.Decrement();
}
}
Two questions now:
Will this Controller Action method UploadFilesAsync(), release this Controller for other Requests once the inside Task thread work completes fully or be available to other Requests right after when the Task just starts executing?
Also what will happen if I make this UploadFilesAsync() method behave in a synchronous manner by applying "Synchronized" attribute on it? example:
[MethodImpl(MethodImplOptions.Synchronized)]
Will this Controller Action method UploadFilesAsync(), release this
Controller for other Requests once the inside Task thread work
completes fully or be available to other Requests right after when the
Task just starts executing?
By "release this controller" I'm assuming you mean release the ASP.NET threadpool thread currently occupied with processing your message. If so, the answer is the latter. It will release the current thread immediately without waiting for the inner task executed to complete.
Also what will happen if I make this UploadFilesAsync() method behave
in a synchronous manner by applying "Synchronized" attribute on it?
MethodImplOptions.Synchronized does not make the method run synchronously. It merely is like wrapping your whole method executing with lock (this). This would mean the that multiple callers won't be able to use the same instance of your Controller, but that doesn't happen anyway, as a new Controller is spun up for every request made. That means it will make no difference whatsoever.
As a side note - You should not be using Task.Factory.StartNew nor Task.Run inside ASP.NET, because any work offloaded to that threadpool thread will not be registered with IIS, which means your thread could be abnormally aborted due to IIS recycling. Instead, look into HostingEnvironment.QueueBackgroundWorkItem if you're using .NET 4.5.2, or look into Stephan Clearys AspNetBackgroundTasks

Task.Run() does not runs asynchronously

I have an ASP.NET MVC 3 site that connects to a WCF service. The WCF Service is independent from the site and is hosted in a Windows Service.
Most of the calls are synchronous, so it's not a problem to wait for the WCF to do it's thing.
However, one of those (already implemented) calls takes a bit too long, and, as it essentially does not output anything directly, I wanted to spin it on the service and forget about it.
So I changed my code from:
public ViewResult StartSlowCalculation(CalculationOptions calculationOptions)
{
WcfServiceProxy.DoSlowCalculation(calculationOptions);
ViewBag.Started = true;
return View();
}
to
public ViewResult StartSlowCalculation(CalculationOptions calculationOptions)
{
Task.Run(() =>
{
WcfServiceProxy.DoSlowCalculation(calculationOptions);
});
ViewBag.Started = true;
return View();
}
which, as I understand should start an asynchronous request, and return immediately. Still, the execution is completely synchronous, and the UI is frozen until the operation concludes.
What obvious thing am I missing?
Update:
Also, note that I would prefer not to change the server implementation to an async one, just to de-synchronize the call to the service on the call-site.
Moreover, I've noticed that the StartSlowCalculation method finishes executing, but the server does not return a response until the service method finishes executing.
The WCF Service Proxy just does:
public void DoSlowCalculation(CalculationOptions calculationOptions)
{
//some logging code
Channel.DoSlowCalculation(calculationOptions);
}
so it's completely synchronous, however that shouldn't matter as it should be executed on an independent thread.
A task operation can run in the calling thread, it depends on taskScheduler decision. To help TaskScheduler make a right decision regarding long running call you can specify task creation option TaskCreationOptions.LongRunning.
And you can check whether task operation is running in a separate thread:
int launchedByThreadId = Thread.CurrentThread.ManagedThreadId;
int launchedInThreadId = -1;
Task.Run(() =>
{
launchedInThreadId = Thread.CurrentThread.ManagedThreadId;
WcfServiceProxy.DoSlowCalculation(calculationOptions);
});
// then compare whether thread ids are different
BTW, are you using any kind of Task.Wait() operation? It will block calling thread as well.
EDIT:
You might find following post interesting Is Task.Factory.StartNew() guaranteed to use another thread than the calling thread?
So try out using Task.Factory.StartNew() and specify cancellation token even you do not need it, sounds weird but it seems this guarantees that task will not be run eventually in the calling thread. Correct me If I wrong.
I've done this before.
The most robust way would be to use Asynchronous Controller's, or better yet an independant service such as a WCF service.
But in my experience, i've just needed to do "simple", one-liner task, such as auditing or reporting.
In that example, the easy way - fire off a Task:
public ViewResult StartSlowCalculation(CalculationOptions calculationOptions)
{
//Some Synchronous code.
Task.Factory.StartNew(() =>
{
WcfServiceProxy.DoSlowCalculation(calculationOptions);
});
ViewBag.Started = true;
return View();
}
That's a simple example. You can fire off as many tasks as you want, synchronize them, get notified when they finish, etc.
For more details you can see this links.
https://msdn.microsoft.com/en-us/library/dd321439(v=vs.110).aspx

How can I return a response from a WCF call and then do more work?

I have a synchronous web service call that returns a message. I need to quickly return a message that basically says that order was received. I then need to spend a couple of minutes processing the order, but cannot block the service call for that long. So how can I return from the web service, and then do some more stuff? I'm guessing I need to fork some other thread or something before I return, but I'm not sure of the best approach.
string ProcessOrder(Order order)
{
if(order.IsValid)
{
return "Great!";
//Then I need to process the order
}
}
You can open a new thread and have it do what you need, while you're main thread returns great.
string ProcessOrder(Order order)
{
if(order.IsValid)
{
//Starts a new thread
ThreadPool.QueueUserWorkItem(th =>
{
//Process Order here
});
return "Great!";
}
}
You could start your big amount of work in a seperate thread
public string ProcessOrder(Order order)
{
if(order.IsValid)
{
System.Threading.ParameterizedThreadStart pts = new System.Threading.ParameterizedThreadStart(DoHardWork);
System.Threading.Thread t = new System.Threading.Thread(pts);
t.Start(order);
return "Great!!!";
}
}
public void DoHardWork(object order)
{
//Stuff Goes Here
}
Is the work you're doing "important?" I assume it is. You could use a thread, but you'll have to be ok with the possibility that your work might get interrupted if the machine restarts or possibly if the asp.net worker process recycles. This would likely lead to the work not getting done even though you already told the client you had accepted it. This might be or not depending on your use case.
I would consider taking the work item you receive from the synchronous service request and putting it in a persistent queue. An easy way to do this is to use a transational MSMQ queue. Your synchronous service puts the work request in the queue and you have a few worker threads pulling work requests out of the queue. Wrap your queue read and the work in a transaction and don't commit the transaction until the work is completed. If you machine or process shuts down in the middle of a request, it will be restarted automatically the next time it starts up.
You could also look at utilizing the PIAB (Policy Injection Application Block) to accomplish work after a method call.

Categories