Search code examples
c#asp.netmultithreadingtaskwait

Threading .StartNew() and .Wait()


I don't even pretend to understand threading in C#, so I'm looking for a really simple explanation on threads - more specifically the StartNew() feature in .NET 4.

I use the following to call another method with a sort of fire-and-forget approach. I might do this when the web page needs to initiate a time-consuming process, but where the page doesn't really need to wait for the result. As I understand it, the DoSomething() method will run "asynchronously."

System.Threading.Tasks.Task.Factory.StartNew(() =>
{
    DoSomething();
});

The above code seems to work just as I want it to - although I have a sneaky suspicion that I'm not really using it for its intended purpose. I've also been caught-out using it in Console Applications that close before the DoSomething() method has run. I'm not sure why it works on web pages but not console apps - I'm assuming because the app pool continues to run even when the page has been unloaded.

The thing that really confuses me is that I've seen a lot of examples where they end such code with .Wait(). As I understand it, the .Wait() means that the rest of the code on this page will wait until the DoSomething() method has run... in which case - in my limited understanding - this seems to negate any benefit of using the StartNew() in the first place?

I hope somebody can help explain this in a way my simple little mind will understand! Perhaps you can also explain a better way of calling a method with a fire-and-forget approach? Many thanks!


Solution

  • System.Threading.Tasks.Task.Factory.StartNew(() =>
    {
        DoSomething();
    })
    

    This will essentially offload the work onto the ThreadPool. A ThreadPool thread is used once one is available (this means, it could actually be executed at different times, if the ThreadPool is filled).

    By calling .Wait() at the end, this will block until that task has finished executing. Essentially, it will not let the main thread to run until it's done, and can lead to performance issues depending on how long DoSomething() takes to execute.

    A better approach is to use async/await so that your application can continue to be responsive without tying up the main Thread.

    Also note that you should be using Task.Run instead of StartNew. Unless you need to provide other paramters (such as a cancellation token, schedule, etc.), Task.Run is the default usage.

    For example:

    // fire and forget, this will run on a thread once one is available on the thread pool
    Task.Run(()=>
    {
        DoSomething();
    });
    
    // fire and wait until exeuction has finished. this will block all activity until it is done
    Task.Run(()=>
    {
        DoSomething();
    }).Wait();
    
    // fire and and wait until the task is completed but do not block the current thread.
    private async void DoSomethingAsync()
    {
        await Task.Run(()=>
        {
            DoSomething();
        });
        // perform work after
    }
    

    NOTE: As VMAtm noted, "starting a task in ASP.NET app could be dangerous as it can be suspended with thread by IIS itself".

    Why is it dangerous?

    • An unhandled exception in a thread not associated with a request will take down the process.
    • If you run your site in a Web Farm, you could end up with multiple instances of your app that all attempt to run the same task at the same time.
    • The AppDomain your site runs in can go down for a number of reasons and take down your background task with it.

    More info: http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/

    How to run tasks on ASP.NET app: http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx