for a task, potentially taking too long to complete, I'd like a mechanism
to start the task
return back to user interface (its a web page)
periodically/randomly check if the task is complete
cancel the executing task when user wishes so
get notified when the task completes / fails
what are the possible solutions?
Threads?
Start a thread, save its ManagedThreadId, (can you get a thread by its id)
write a windows service,
send the request to service via shared objects/files/db?
keep interacting with the service the same way (objects/files/db,etc)
Services?
Host a WCF Service in a Windows Service that will perform the background tasks by adding/reading from a queue which can be maintained either using MSMQ or in a database.
When you add an item for processing; you should get a task id. You should be able to then log the completion/failed/cancel status of the task in db against the task id.
You can have following methods in your WCF contract
int ProcessItem(ItemDetails details); // returns task id
bool CancelTask(int taskID); // returns true if successfully cancelled; false otherwise
TaskStatus GetTaskStatus(int taskID); // returns Cancelled, Waiting, Failed or Completed
You can do that in single process
Have a look at the Task Paralell Library and / or TPL DataFlow
http://msdn.microsoft.com/en-us/library/dd460717.aspx
http://go.microsoft.com/fwlink/?LinkId=205053
Related
In Azure batch services, when a task is added to a job, you are able to create a TaskStateMonitor which will wait for the completion/timeout of the added task. When a task completes (or timeouts), the monitor will return (or raise an exception).
What happens to the monitor if you delete the job before completion? Does the task monitor wait until the timeout is reached, does it raise a different exception, or does it consider the job as being completed?
Figured it out: After testing this on my end, The TaskStateMonitor will raise the following exception when it checks for a state and cannot find one
Microsoft.Azure.Batch.Common.BatchException: Operation returned an invalid status code 'NotFound'
I am trying to implement a task in fire and forget manner.
Lets look at the below piece of code.
public IHttpActionResult Update(int id)
{
var updatedResult = _updater.update(id);
// fire and forget a task
sendEmailToUser();
return ok();
}
private async Task sendEmailToUser()
{
var httpclient = new HttpClient();
// assume the client is initiated with required url and other headers
await httpclient.postasync("some url");
}
Given the above code, can i safely assume that whenever Update endpoint is called, sendEmailToUser task is triggered and will be run to completion ?
No. You should almost never start any background threads in web application. HTTP is suppose to be stateless and the web server was designed with that in mind.
The server might be put into sleep state when there is no incoming request for a set period of time. During that time all the background execution will be halt including the one you had. It might and might not get resume when the next request comes in.
Or when IIS decides to recycle your App domain on a scheduled basis your thread will get killed too.
If you really need background tasks then do that using windows service or run it as a separate console application.
Under normal conditions, it's reasonable to expect that the task will run to completion. It will go on independently.
Your biggest concerns, in this case, should be about the web API not being terminated, and the task not throwing an exception.
But if OP needs to be 100% sure, there are other safer ways to code that.
In asp.net Web API how can I call long running blocking actions asynchronously?
I have a web api controller action that needs to make a relatively (10 seconds, as an example) long running DB call. This seems like a candidate for an asynchronous method. I can offload the long running task onto a new thread, and unblock my asp.net request thread to handle some other requests, so my simplified controller action would look like this:
public async Task<IHttpActionResult> Get()
{
IEnumerable<Thing> things = await Task.Run(() => DoLongDbCall());
return Ok(things);
}
I have encountered a couple of blogs (this one, for example) that suggest this may not be an optimal way to achieve this in asp.net. The author suggests using Task.FromResult() and doing the DB call synchronously, but i cant see how this helps; My request thread is still going to be blocked waiting for the DB call to return.
First, consider what happens with a synchronous call:
public IHttpActionResult Get()
{
IEnumerable<Thing> things = DoLongDbCall();
return Ok(things);
}
A request comes in, and ASP.NET grabs a thread pool thread to handle the request. This thread invokes the Get method, which does the work. One thread pool thread is used during the entire request.
Now, let's walk through what happens in the current code (using Task.Run):
public async Task<IHttpActionResult> Get()
{
IEnumerable<Thing> things = await Task.Run(() => DoLongDbCall());
return Ok(things);
}
A request comes in, and ASP.NET grabs a thread pool thread to handle the request. This thread invokes the Get method, which then grabs another thread pool thread to do the work and returns the original thread pool thread back to the thread pool. One thread pool thread is used during the entire request (and two thread pool threads are used for a very brief period of time).
So, the Task.Run code forces an extra thread transition without providing any benefit (the entire point of using async on the server side is to free up threads). This is why I recommend against using Task.Run (or any other way to run work on the thread pool) on ASP.NET.
A proper asynchronous solution would be to use asynchronous DB calls:
public async Task<IHttpActionResult> Get()
{
IEnumerable<Thing> things = await DoLongDbCallAsync();
return Ok(things);
}
A request comes in, and ASP.NET grabs a thread pool thread to handle the request. This thread invokes the Get method, which then starts the asynchronous operation and returns the thread pool thread back to the thread pool. Later, when the db call completes, ASP.NET grabs a thread pool thread to finish the request. For most of the request, no thread pool threads are used (one thread pool thread is used for a brief period of time at the beginning and end of the request).
my request thread is still going to be blocked waiting for the DB call to return.
That's not completely true:
Your request will still wait for the long operation to end, but your thread will be free to process other operations.
You must keep in mind that IIS has a reduced number of available threads (especially if running under non server systems) and freeing one that is not needed is always a good thing.
Moreover, if you have shortage of threads, and if you use Task.Run, the asynchronous operation will wait for an available thread, and if you don't release your current one, you'll end up with an horrible deadlock.
I have a WCF service set to PerCall
I would like to know how I can send a Start call from the client to start a long running process, and send a Cancel command to cancel it
My WCF service looks something like this
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Service1 : IService1
{
CancellationTokenSource cancelToken = new CancellationTokenSource();
public void Start()
{
var compute = Task.Factory.StartNew(StartLongRunningTask, cancelToken.Token);
}
public void Stop()
{
cancelToken.Cancel();
}
private void StartLongRunningTask()
{
//process here
}
}
I guess the problem here is that, each time a call comes to the server, it's treated as a new request.
So how should starting and cancelling a long running task in WCF be done?
EDIT: I'm hosting it as a windows service
I have a WCF service set to PerCall
... the problem here is that, each time a call comes to the server, it's treated as a new request.
Yup, that's exactly what you're telling it to do. If you can, just change to InstanceContextMode.PerSession; then you can do what you're trying to do (assuming you're self-hosting).
If you can't do this, then you'll have to develop a more complex solution like #PeterRitchie commented. First, your host: IIS is not designed to have long-running operations independent of requests, so I'll assume you're self-hosting. Next, you'll need a form of token (like a GUID) that will act as an identifier for a long-running operation. Your Start method will allocate a GUID and CancellationTokenSource and start the operation, and your Stop method will take a GUID and use that to look up the CancellationTokenSource and cancel the operation. You'll need a shared (static, threadsafe) dictionary to act as lookup.
If your host is IIS, then your solution gets more complex... :)
First, you'll need a backend that's not hosted in IIS. Common choices are an Azure worker role or a Win32 service. Next, you'll need a reliable communications mechanism: an Azure queue, MSMQ, WebSphere, etc. Then you can build your WCF-over-IIS service to have the Start method generate a GUID identifier and drop a message on the queue to start processing. The Stop method takes the GUID and drops a message on the queue to cancel processing. All other logic gets moved to the backend service.
From how you've asked, the client seems to be aware of the async nature of the request.
#StephenCleary and #PeterRitchie's points are excellent, but your first step is to re-do your service/contract to properly implement an async service and add the means of communicating back (to client) some information/handle to the long running operation.
The Framework contains several paradigms for asynchronous programming (already :-) )but when it comes to WCF, you kinda fall back to How to: Implement an Asynchronous Service Operation
That will provide some infrastructure, but not necessarily the ability to automatically cancel an operation.
Speaking strictly about the cancellation (as this is your question): you will have to extend whatever your solution ends up being for cancellation. At the minimum you need to add necessary logic to your service “worker” to monitor and honor the cancellation token.
Other considerations that you may expect to encounter: return result from cancellation; cancelling a task that has managed to complete (what of you updated the 1,000,000 records by the time the cancellation request came); exception handling (with task-based programming exceptions are not thrown, but bundled in the Task, or whatever other “vehicle” you use to describe the ongoing operation).
We are dealing with processing and uploading of large files in windows service (.net 4.0). Process and upoload steps can take minutes to complete. Admin has the ability to mark job as cancelled, directly in database but to clear in memory queue it requires service restart. Goal is to abandon that job and pick next job in queue without service restart. Here's what I want to do:
In main entry point, start two tasks:
Task processTask = Task.Factory.StartNew(ProcessJob);
Task monitorTask = Task.Factory.StartNew(MonitorDB);
ProcessJob would call multiple long running steps like ProcessFile, UploadFile. We are checking for job status between the steps but job may be stuck in one of these long running steps.
If monitorTask detects job status change in DB, it should communicate that to main thread (through exception or message), so that main thread can quit, removing itself from the processing queue and allow next job in queue to start. Without Wait cannot get to exception but cannot wait because need to run these tasks in parallel.
At this time we are not concerned with the fact that some synch step in ProcessJob may still be going on and it may eventually complete. We would handle that in code.
So far in all of my applications, I have used Task.ContinueWith for success and failure, but never had to communicate back to main thread.
You could communicate between the monitor thread and the main thread through a BlockingCollection<T>.
Here's a simple example I wrote.
BlockingCollection<string> queue =
new BlockingCollection<string>();
// monitor thread.
Task.Factory.StartNew(() =>
{
while (true)
{
Thread.Sleep(1000);
queue.Add("event occured.");
}
});
// main thread.
while (true)
{
// blocks when no messages are in queue.
string message = queue.Take();
// kill process thread here.
}
You can use a CancellationToken for this. If you look at the Task class, there is an overload where you can pass in a CancellationToken. Store a reference to this token in your service and simply have your upload/process routine periodically check if the token has been cancelled like so:
if (yourToken.IsCancellationRequested)
break;