views:

95

answers:

5

Hi everyone,

I want to implement such logic in my asp-net-mvc application:

user clicks a button ->

server executes some time-consuming logic in ~15 threads (i get data from really slow independent sources) ->

when all work is done, server merges the results and passes it back to user

The other day i've seen an article which explained why creating new Threads in asp-net application is highly not recommended, and ThreadPool is the one that should be used. What are best practices for mvc in this case? Why shouldnt i create my threads, backgroundworkers, tasks, whatever by myself and use threadpool? The Application will be hosted on a public server, if it matters.

Thanks, Ilya.

+1  A: 

Because a ThreadPool is designed to do that:

It provides a pool of threads that can be used to execute tasks, post work items, process asynchronous I/O, wait on behalf of other threads, and process timers.

I guess it's better to store the results on your server in a database when they are done.

And then you could use a timer and AJAX to periodically request if the process is done, if so, get data.

TomWij
+2  A: 

You shouldn't create your own threads because this has significant overhead. Creating a new thread is expensive. So the question should be: why shouldn't you use the threadpool? It has a number of threads ready for use. An even better approach would be to use the task parallel library, it provides a layer of abstraction above the threadpool that makes it a lot easier to work with threads.

You should realize that the only (easy) way to accomplish this, is by having the user wait until all work is done and then serve the page. A more complicated but also more responsive approach would be to use Ajax (via jQuery for example) to ask the server periodically how far the work has progressed. This way you can also provide some progress indicator.

Ronald Wildenberg
This would be an operation perfomed by admin/moderator, so its not that nessesary :)
portland
+3  A: 

Yes JQuery and some AJAX will do it most properly. Load the Page, then send ~15 separate Ajax queries back to the Server and let them finish asynchronously. This way you can let the web-server handle Threading (which is does well) and concentrate on displaying either a ticker or a virtual progress bar to the user while waiting.

Rune Baess
As long as the work can be broken down this way, then this is a very good approach... in addition to avoiding the pitfalls of roll your own threads, it's a lot easier to give the user feedback as it goes along.
Jim Leonardo
Whether this is a good approach depends on whether you want to merge the results on the server or on the client. The risk exists that you need to move a lot of logic to the client that could be better handled on the server.
Ronald Wildenberg
+1  A: 

If you're using .Net 4, I would even recommend looking at the parallel namespaces. They make this even simpler and do a better job of utilizing all your CPU cores.

I would also look at offloading this from your main web app altogether. Having a separate set of services or a message queue to handle this long running request will let you scale a lot more easily and will allow your web app to worry about servicing page requests, not performing long running logic. Google up something like "iis long running request" to get started.

Jim Leonardo
+2  A: 

This seems like a really good place for using the new AsycController in ASP.NET MVC 2. It is really easy to use and lets you run queries against multiple independent sources without blocking request threads.

MSDN has a great example where they are querying a news service, a weather service, and a sports service.

You can see in the original code that they are querying each source sequentially, but in the final version, all the tasks run in parallel and control returns to the controller when they are all completed:

public void IndexAsync(string city)
{
    AsyncManager.OutstandingOperations.Increment(3);

    NewsService newsService = new NewsService();
    newsService.GetHeadlinesCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["headlines"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    newsService.GetHeadlinesAsync();

    SportsService sportsService = new SportsService();
    sportsService.GetScoresCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["scores"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    sportsService.GetScoresAsync();

    WeatherService weatherService = new WeatherService();
    weatherService.GetForecastCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["forecast"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    weatherService.GetForecastAsync();
}

public ActionResult IndexCompleted(string[] headlines, string[] scores, string[] forecast)
{
    return View("Common", new PortalViewModel  {
        NewsHeadlines = headlines,
        SportsScores = scores,
        Weather = forecast
    });
}
Rohan Singh