LoveMeSomeCode
LoveMeSomeCode

Reputation: 3947

ASP.NET worker thread after response is sent

I'm trying to do something a bit outside the box here and I'm getting the kind of hit-and-miss results that make think it's a threading issue.

We have an ASP.NET 4 app where want to log each page request in the database with some session info. We also want to see how long each page took to generate, so we have base page that all pages in the app derive from and we override it's OnUnload event. In that handler we get the page generation time from:

DateTime.Now.Subtract(HttpContext.Current.Timestamp).TotalSeconds

this works. But then we decided to try it async; maybe the page could be delivered faster if the client didn't have to wait on the insert of that log record. So I tried something like this:

ThreadPool.QueueUserWorkItem(new WaitCallback(LogPageRequest));

But as I said, sometimes it works, sometimes it doesn't. Is the server killing off my worker thread because the request is finished? Is there a better way to do this?

Upvotes: 0

Views: 360

Answers (2)

Stephen Cleary
Stephen Cleary

Reputation: 457362

ThreadPool.QueueUserWorkItem should never be used in ASP.NET. The problem is that the thread pool thread may or may not have a page context (and if it does have a page context, it may be for another page!). It's also entirely possible that the callback never runs (e.g., any time the app pool is recycled).

BackgroundWorker and other SynchronizationContext-based solutions work because they restore the page context (and identity and culture) on their callbacks; see my MSDN article for how SynchronizationContext works with ASP.NET (and why). However, they also block the page request until they complete.

If you want to do some operation after the page request is completed, you should use a separate service for that. A local WCF or Windows Service would be a good solution.

Upvotes: 1

aL3891
aL3891

Reputation: 6275

I think it's not the worker thread that is getting killed but the HttpContext.Current instance. Try putting that timestamp value in its own variable before queueing it to the threadpool ( in other words, create a closure around it ) That way the intended value will still be around when the thread pool is ready to execute the logging method

...
// generate response
var time = DateTime.Now.Subtract(HttpContext.Current.Timestamp).TotalSeconds; 
ThreadPool.QueueUserWorkItem( o => {
  SaveTimeToDatabase(time);
});

//return response

if you dont want to use closures you can also pass the time value to ThreadPool.QueueUserWorkItem:

var time = DateTime.Now.Subtract(HttpContext.Current.Timestamp).TotalSeconds; 
ThreadPool.QueueUserWorkItem(new WaitCallback(LogPageRequest), time);  // the object passed into LogPageRequest will now be the time to save to the database
...

Upvotes: 0

Related Questions