Long Running Tasks and Threads

Posted on December 13, 2012


There appears to be some confusion and/or uncertaintly on whether a TPL task with TaskCreationOption.LongRunning always will run on it’s own thread or occasionally execute on either the caller’s thread or a ThreadPool thread. I suspect a lot of it comes from the choice of language in the MSDN documentation:


Specifies that a task will be a long-running, coarse-grained operation involving fewer, larger components than fine-grained systems. It provides a hint to the TaskScheduler that oversubscription may be warranted. Oversubscription lets you create more threads than the available number of hardware threads.”

The important word here is ‘hint’. The TaskCreationOptions.LongRunning flag ‘hints’ to the scheduler that it would like to to run on a dedicated thread. Hinting obviously isn’t strong enough for most developers (it shouldn’t be) who wants guarantees that using the TPL won’t accidentally cause deadlocks and other nasty threading sideeffects in their code hence they assume that the creation of a new dedicated thread is optional in the default framework. This assumption is incorrect as the default TaskScheduler (which most developers will be using) always creates a new non-pooled thread for long running tasks.

To prove it I went through the pain of downloading the .NET source code and figuring out what extension the downloaded “aspx” file actually should have. Turns out it was meant to be “.msi”. Obviously.

From ThreadPoolTaskScheduler.cs (the default TPL scheduler)

protected internal override void QueueTask(Task task)
    if ((task.Options & TaskCreationOptions.LongRunning) != 0)
        // Run LongRunning tasks on their own dedicated thread.
        Thread thread = new Thread(s_longRunningThreadWork);
        thread.IsBackground = true; // Keep this thread from blocking process shutdown
        // Normal handling for non-LongRunning tasks.
        bool forceToGlobalQueue = ((task.Options & TaskCreationOptions.PreferFairness) != 0);
        ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue);


The reason the word ‘hint’ was used, I assume, is because the TaskScheduler is pluggable and no guarantees can be made for custom scheduler implementations. Which makes sense but is why, I believe, the confusion remains. Anyhow I hope I have shown that for the default scheduler it is safe to use long running tasks and if you are the kind of developer that replaces the default TaskScheduler you either know what you are doing or you have bigger problems to deal with than worrying about what thread your long running tasks are running on.

Glad Lucia

Tagged: ,
Posted in: .NET