I’m hoping to mold this post into a legitimate StackOverflow question, as I’d really like a solid understanding of this scenario, but I could definitely see if it’s too localized or considered an “opinion”.
Here’s my scenario: When I load my web app, I load a whole bunch of data from the database and cache it. The problem is, this process takes about 10-15 seconds and creates a lag when the web server first starts up. This is kinda annoying while developing, and also causes a few issues when I bounce the web server in production (since this is a new site, I’m often hot fixing small bugs as I find them, or mucking with IIS settings).
I got to wondering – Can I offload this work into a new thread when the application starts, and have this going on in the background as other users are using the site? Obviously, certain features wouldn’t work for about 10-15 seconds when the site loaded, but I can handle that condition or block until the data becomes available. At first, I was thinking no. The web server was going to either terminate these threads if the request ended, or block until these threads finished. I decided to write a little test app to test this theory:
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
Thread thread = new Thread(LoadData);
thread.Start();
}
private void LoadData()
{
for (int i = 0; i < 100; i++)
{
Trace.WriteLine("Counter: " + i.ToString());
Thread.Sleep(1000);
}
}
}
When the app starts, I launch a new thread, and just have it count to 100. Much to my surprise, I immediately got to the home page and in my Visual Studio debug output window, I could see the incrementing numbers count up. I was actually surprised this works.
My Questions:
First off, are there any gotchas with doing this? Is it asking for trouble and is anything going to blow up? Will this behavior change between web servers, or versions of IIS as perhaps they use different threading models? I’m looking for overall feedback on this design.
tl;dr: What you’re doing is probably fine, and a common way of dealing with this situation.
There are certainly gotchas with threads. Not saying that you shouldn’t use them in this case, but you are wise to make sure you understand them. To abuse an old meme: “Programmers will often see a concurrency problem, and solve it by using another thread. Now they have two problems.”
If your thread is meant to be a background thread (meaning that it shouldn’t prevent your site from shutting down if the thread is still running), make sure the IsBackground property is set. Better yet, use a BackgroundWorker.
How usable is your site before the initialization thread has finished? Is there any real reason to show an interface while it is initializing?
I’ve gone full-circle on directly using threads for concurrency – from being scared of them, to thinking I understood them and embracing them, to thinking I understand them and being scared of them. Right now, when I have a concurrency/async problem that I want to solve, I usually try to create an architecture composed of multiple, inter-communicating, concurrent (but single-threaded) processes. A lot of things get simpler that way, and I’ve had a lot of success with it.
In your current case, using a background thread to handle the database load is probably safe enough if you can assure that you don’t access the resources that the thread is preparing before they are ready. However, (if you can target .Net 4.0), a better way to do what you want would be to leverage the Task Async library – which is to say, write your routines to be asynchronous, and return a Task of T instead of T itself. Done correctly, this can allow your site to be responsive to the user, even on an single-threaded environment, and even while it is still loading resources in the background.