views:

237

answers:

8

I've got a single statement running on a ASP.NET page that takes a long time (by long I mean 100ms, which is too long if I want this page to be lightning fast) , and I don't care when it executes as long as executes.

What is the best (and hopefully easiest) way to accomplish this?

A: 

If you mean to say that the response can be sent before the command is ran, you could use ThreadPool.QueueUserWorkItem to run it on another thread without blocking your request.

Frank Schwieterman
+1  A: 

There's a background worker thread that can be very useful when processing information in the background of a .NET application. A bit more context would help answer the question better.

JoshCaba
What context would help? I didn't want to make the question *so* narrow that it would be of no use to posterity.
Larsenal
I was wanting to give the best answer possible for your particular situation, but all of the other suggestions that were mentioned covered the other options I was debating between.
JoshCaba
+3  A: 

If it's 100ms, then don't bother. Your users can't detect a 100ms delay.


Edit: some explanations.

If I remember correctly, 100 milliseconds (1/10 second) is near the minimum amount of time that a human can perceive. So, for the purpose of discussion, let me grant that this 100ms can be perceived by the users of the OP's site, and that it is worthwhile to improve performance by 100ms. I assumed from the start that the OP had correctly identified this "long-running" task as a potential source of a 100ms improvement. So, why did I suggest he ignore it?

Dealing with multiple threads properly is not easy, and is a source of bugs that are difficult to track down. Adding threads to a problem is usually not a solution, but is rather a source of other problems you simply don't find right away (*).

I had the opportunity once to learn this the hard way, with a bug that could only be reproduced on the fastest eight-cpu system available at the time, and then only by pounding on the thing relentlessly, while simulating a degree of network failure that would have caused the network administrators to be lined up and shot, if it had happened in real life. The bug turned out to be in the Unix OS kernel handling of signals, and was a matter of the arrangement of a handful of instructions.

Granted I've never seen anything that bad since then, I've still seen many developers tripped up by multithreading bugs. This question, seemed to be, on the one hand, asking for an "easy way out" via threading, and on the other hand, the benefit was only 100ms. Since it did not appear that the OP already had a well-tested threading infrastructure, it seemed to me that it made better sense to ignore the 100ms, or perhaps to pick up performance some other way.


(*) Of course, there are many circumstances where an algorithm can profitably be made parallel and executed by multiple threads, running on multiple cores. But it does not sound like the OP has such a case.

John Saunders
Downvoter: Care to share the reason for the downvote?
John Saunders
Why do you like Google so much? I'm glad they don't waste 100ms of my time 50 times a day.
spoon16
I downvoted because your answer is not an answer to the question. The OP didn't ask "Am I wasting my time optimizing for 100ms?" he asked how he could remove the 100ms delay. I build cloud scale web services and our average response time must be significantly below 100ms, ignoring this low hanging fruit that the OP has already identified seems lazy.
spoon16
Do you use a "spawn off a single statement" technique to improve performance? Probably something a bit more sophisticated. My thought was, if the OP is looking to go on the cheap with threading, then he should ignore 100ms. If he wants to shave off 100ms, then he wants a better threading strategy than "let's make this one statement execute async".
John Saunders
BTW, thanks for answering. It's always better to know the reason for a downvote. I should no doubt have said in my answer what I just said in my reply to you.
John Saunders
I'll remove the downvote if you update your answer ;)
spoon16
@spoon16: I'm only now seeing your comment about Google. I don't actually use Google much, but I doubt my feelings towards it are due to 100ms. If I remember correctly, that's right on the border of what a person can perceive. In the absence of a better multithreading framework, I'd prefer the OP concentrate on everything else before tackling this last 100ms through something as dangerous as multithreading.
John Saunders
I agree, dumping something on the threadpool is not the right approach.
spoon16
+1  A: 

// This is the delegate to use to execute the process asynchronously
public delegate void ExecuteBackgroundDelegate();

//
[STAThread]
static void Main(string[] args)
{         
    MyProcess proc = new MyProcess();

    // create an instance of our execution delegate
    ExecuteBackgroundDelegate asynchExec = new ExecuteBackgroundDelegate(proc.Execute);

    // execute this asynchronously
    asynchExec.BeginInvoke(null, null);
}
Chris Gallucci
You could also just us an Action... new Action(() => proc.Execute).BeginInvoke(null, null);
spoon16
Wow, thanks! I didn't know about that. I just realized I don't have a single C# language reference past 1.1.
Chris Gallucci
+8  A: 

The easiest way is probably to get it to execute in the threadpool. For example, to make this asynchronous:

using System; using System.Threading;

class Test
{
    static void ExecuteLongRunningTask()
    {
        Console.WriteLine("Sleeping...");
        Thread.Sleep(1000);
        Console.WriteLine("... done");
    }

    static void Main()
    {
        ExecuteLongRunningTask();
        Console.WriteLine("Finished");
        Console.ReadLine();
    }
}

Change the first line of Main to:

ThreadPool.QueueUserWorkItem(x => ExecuteLongRunningTask());

Be aware that if you pass any arguments in, they'll be captured variables - that could have subtle side-effects in some cases (particularly if you use it in a loop).

Jon Skeet
..what was i thinking trying to beat you.. :D
Stan R.
That worked (in concept) but something about executing that code on a different thread crashed the process. I substituted the Thread.Sleep to confirm that I at least had the mechanics correct. Are there particular ASP.NET things that get out of whack when you use this approach? For instance, that code may read web.config behind the scenes. Would that throw it off?
Larsenal
I'm getting all sorts of interesting side effects. Trying to open a System.Data.SqlClient.SqlConnection is failing with the message "Login failed for user...'XYZ'." Interesting stuff to track down.
Larsenal
I hate to say it, but I feel vindicated. @Larsenal: when you've tracked down and fixed these bugs, what are the chances you've found them all? I'm concerned you're trading 100ms performance improvement for weeks or months of bugs. From now on, anytime something strange happens, you'll be blaming the threading code.
John Saunders
@Larsenal: Don't forget that when you're not on the "main" thread you won't have anything to do with the current request etc, which are threadstatics.
Jon Skeet
A: 

you can use a ThreadPool.

ThreadPool.QueueUserWorkItem( o => Thread.Sleep(1000) /*your long task*/  );
Stan R.
A: 
class Test
{
    void LongRunningTask()
    {
        Console.WriteLine("Sleeping...");
        Thread.Sleep(10000);
        Console.WriteLine("... done");
    }

    static void Main()
    {
        Test t = new Test();
        new Action(() => t.LongRunningTask()).BeginInvoke(null, t);
    }
}
bruno conde
A: 

I wouldn't bother with any of that threading stuff for a 100ms delay. This will do fine:

protected void Page_Unload(object sender, EventArgs e)
{
    HttpContext.Current.Response.Flush();
    HttpContext.Current.Response.Close();

    // Your code here
}

(copied from my earlier answer for this question)

Since the connection to the client downloading your page will be closed, their browser will stop displaying a loading message. Your code will continue execution normally however.

Thorarin