How should I perform a long-running task in ASP.NET 4?
I am building a website u开发者_运维百科sing .NET 4. There are lots of MSDN articles dating from 2003, about using Thread objects and 2007, using Asynchronous Pages in .NET 2, but that is all pretty stale. I know .NET 4 brought us the Task class and some people vaguely cautioning against its use for this purpose.
So I ask you, what is the "preferred" method circa 2011 for running background/asynchronous work under IIS in ASP.NET 4? What caveats are there about using Thread/Task directly? Is Async=true still in vogue?
EDIT: Ok, ok, from the answers it's clear the opinion is that I should make a service if I can. But the advantages to doing it inside the webapp are significant, especially easier deployment/redeployment. Assuming the process is safe-to-crash, then, if I were to do it inside IIS, what is the best way?
Preferentially, avoid having long tasks executing in such an environment.
Delegate long running tasks out to a stable system service via interoperability, leaving the web application responsive and only required for direct user requests.
Web applications have never been (and still aren't) considered reliable systems - anyone who has ever used a browser has encountered (at least) a time-out, to be sure; and such inconvenience (for both parties) is not limited to this scenario. Of course, any system can crash, but the circumstances surrounding such an event on a system built-to-be-persistent ought to completely exceptional.
Windows services are designed to be long running, and if something goes wrong you've generally got more to worry about than your individual service.
It's best to be avoided, but if you are forced to, consider Hanselman's thoughts at How to run Background Tasks in ASP.NET.
Among them, and for something quick and easy, I would suggest you look in particular at the QueueBackgroundWorkItem added in 4.5.2.
From personal experience, Task does not cut it. QueueBackgroundWorkItem is much better.
You can create a static ThreadPool like this http://www.dotnetperls.com/threadpool with limited threads number(for example only 2). and then queue tasks in it, but it's highly not recommended because web servers are not for such kind of tasks
My preferred method is the same as Robert Harvey proposes in his answer.
You can still use the Task Parallel Library, but spin the task up in a separate process outside of IIS (the reason being that IIS has a limited number of worker threads to hand out and imposes other limitations that can make long running tasks unpredictable).
This is a description of a 'once a day' scenario.
If you really want to avoid creating a service, you could start a timer with 1 minute intervals. Each time the timer delegate is invoked, you will have to run something like this (pseudo code):
lastInvokeDay = LoadLastInvokeDate();
If (lastInvokeDay < DateTime.Now.Date && timeOfDayToRun == DateTime.Now.Time)
{
try
{
today = DateTime.Now.Date;
runMyTask();
}
catch..
finally
{
lastInvokeDay = today;
SaveLastInvokeDay(lastInvokeDay);
}
}
Keep in mind that the lastInvokeDay should be persisted either in Database or on a file...
Now, If you want to enable immediate invocation of the task, you could simply call runMyTask()
on demand.
If its important for you to keep the runMyTask from occuring more than once a day, you could create a syncronized block of code inside it (with a lock
statement) and move the lastInvokeDay
check inside.
Does this answer your question?
I could suggest a simple solution, which doesn't use Windows Services, yet is able to invoke a task to be executed outside of the IIS sandbox. Also it could be easily adopted by any other language or mix of them, in my case that was Python
Create Event log and source on the IIS server (requires Administrative rights), executing from the PowerShell console:
[System.Diagnostics.EventLog]::CreateEventSource('Automations', 'Automations')
If you have no Administrative rights, skip this step. You will fallback to use Windows/Application log
Create a Task Scheduler task to be executed on event, for example, with ID = 2020, Log = 'Automations' and Source = 'Automations'. There you could invoke whatever you like with all necessary permissions
Prepare a code to send your event, while handling a web request. Giving you a Python example, but you could adopt it to your language:
import win32evtlog app_name = "Automations" event_id = 2020 event_category = 0 event_type = win32evtlog.EVENTLOG_INFORMATION_TYPE messages = ['Starting automation'] # Logs event into the custom Automations log if it exists, # otherwise logs event into Windows/Application log handle = win32evtlog.OpenEventLog("localhost", app_name) win32evtlog.ReportEvent(handle, event_type, event_category, event_id, None, messages, None)
Profit
精彩评论