6

I have an ASP.NET MVC 3 (.NET 4) web application.

I have a [HttpPost] action method which submit's some data to the database.

Now, after this method is finishing persisting to the repository, i wish to execute a "background" task (think auditing, or sending an email, etc), where i don't care about the result (unless an error occurs, in which case i'll perform logging).

How can/should i fire off this task from my action method?

[HttpPost]
[Authorize]
public ActionResult Create(MyViewModel model)
{
   if (ModelState.IsValid)
   {
      _repo.Save(model); 
      // TODO: Fire off thread
      return RedirectToRoute("Somepage", new { id = model.id });
   }
   return View(model);
}
Gabe
  • 84,912
  • 12
  • 139
  • 238
RPM1984
  • 72,246
  • 58
  • 225
  • 350
  • You might want to consider a service bus like [nServiceBus](http://NServiceBus.com) or use MSMQ as described here - http://dotnetslackers.com/articles/aspnet/Sending-email-from-ASP-NET-MVC-through-MVC-and-MSMQ-Part1.aspx – Josh Anderson Mar 29 '11 at 01:12

3 Answers3

5

The new .NET 4 way to do this is with a Task.

http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx

Task.Factory.StartNew(MyBackgroundAction);

But for this simple operation where you just want to run it and don't care about coordinating tasks, it's just as easy to use the ThreadPool.

ThreadPool.QueueUserWorkItem(MyBackgroundAction)

You want to avoid creating a new Thread yourself for every action--it'll take up more resources and is unnecessarily wasteful.

If you're background tasks are longer running you might want to setup a producer/consumer queue and have a single or set number of constantly running background threads to run the tasks.

Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
  • Thanks for your answer. One clarification - "You want to avoid creating a new Thread yourself for every action--it'll take up more resources and is unnecessarily wasteful" - how else can i do it? where do i create the new thread then? E.g where do i put the `ThreadPool.QueueUserWorkItem(MyBackgroundAction)` call? – RPM1984 Mar 29 '11 at 02:37
  • 1
    @RPM1984 Using QueueUserWorkItem means that the task will be completed by a thread which is already part of the ASP.Net thread pool. These threads are constantly consuming tasks from the queue and completing them, and you can enqueue a new task from anywhere in your code. However, this has major downsides in ASP.Net as linked in my answer. – Chris Pitman Mar 29 '11 at 02:43
  • @Chris - thanks for your help and answer. The thing is, this is a (relatively) simple app that doens't have huge traffic or concurrent users. In the past (version 1), we used `Thread.Start` and it was fine. So i think i'll be going with `ThreadPool.QueueUserWorkItem`. – RPM1984 Mar 29 '11 at 03:34
2

It is in general ill-advised to use the Thread Pool in ASP.Net for asynchronous tasks.

Note that this question is touched on here, although I do not agree with the answer. The accepted answer assumes that you need the work done now, when for an asynchronous task as described by this question can easily be done later. This allows processing to be spread over a longer period of time, useful when load is not uniform (like most web pages).

Spawning threads for every request is also suboptimal, since there is no easy way to bound the number of threads created. 100 spawned tasks will compete with your web application trying to do "real time" work.

Use a messaging system to manage asynchronous tasks that do not need to be immediately run. Each request to process gets sent as a message which is queued up to be processed later. You can then have a separate application on either the local system, or if these tasks take considerable processing on other machines. You can easily spread the load of these tasks too, by running the processor on multiple machines that all pull from the same queue.

In the past I have used MSMQ to good effect.

Community
  • 1
  • 1
Chris Pitman
  • 12,990
  • 3
  • 41
  • 56
0
// Fire off thread
var t = new System.Threading.Thread(() =>
{
   // do whatever
});
t.Start();

In the "do whatever" you should have try/catch that logs any exceptions.

Richard Schneider
  • 34,944
  • 9
  • 57
  • 73