When to use TaskCreationOptions.LongRunning? When to use TaskCreationOptions.LongRunning? multithreading multithreading

When to use TaskCreationOptions.LongRunning?


It can be quantified, the threadpool manager adds an extra thread beyond the optimum when the existing tp threads don't complete soon enough. It does this twice a second, up to the maximum set by SetMaxThreads(). Which has a very high default value. The optimum is the number of processor cores the machine has available, 4 is typical. Running more threads than available cores can be detrimental due to the context switching overhead.

It does this based on the assumption that these existing threads don't make progress because they are not executing enough code. In other words, they block on I/O or a lock too much. Such threads therefore don't use the cores efficiently enough and allowing an extra thread to execute is entirely appropriate to drive up processor usage and get more work done.

So it is "long running" when the thread takes more than half a second. Keep in mind that this is a very long time, it equals roughly 4 billion processor instructions on a modern desktop class machine. Unless you are running computationally heavy code like calculating the value of pi to a gazillion digits, thus actually executing those 4 billion instructions, a practical thread can only take this long when it does block too often. Which is very common, something like a dbase query is often slow and executed on a worker thread and consumes little cpu.

It is otherwise up to you to verify that the assumption that the threadpool manager will make is accurate. The task should take a long time because it isn't using the processor efficiently. Task Manager is a simple way to see what the processor cores are doing in your program, albeit that it won't tell you exactly what code they are executing. You'll need a unit test to see the thread executing in isolation. The ultimate and only completely accurate way to tell you that using LongRunning was an appropriate choice is to verify that your app indeed gets more work done.


Amending Hans' answer which I mostly agree with.

The most important reason to specify LongRunning is to obtain practically guaranteed and almost immediate execution. There will not be any waiting for the thread pool to issue a thread to your work item. I'm saying "practically" because the OS is free to not schedule your thread. But you'll get some share of the CPU and it's usually not going to take a very long time until it happens.

You're jumping in front of the queue by specifying LongRunning. No need to wait for the thread pool to issue 2 threads per second if it's under load.

So you would use LongRunning for things that must happen not necessarily in the most efficient way but in a timely and steady way. For example, some UI work, the game loop, progress reporting, ...

Starting and stopping a thread costs on the order of 1ms of CPU time. This is by far more than issuing thread pool work items. I just benchmarked this to be 3M items issued and completed per second. The benchmark was quite artificial but the order of magnitude is right.

LongRunning is documented to be a hint but it's absolutely effective in practice. There is no heuristic that takes your hint into account. It's assumed to be correct.


when to specify a task as long-running

It depends what task is doing. If task contains while(true) {...} and lives until application shutdown, then it make sense to specify LongRunning. If you create task to queue some operation and prevent blocking current thread then you don't really care (do not specify anything).

It depends on what other tasks are doing. It doesn't matter if you run few tasks with or without LongRunning. But it might be a problem to create thousand of tasks, where each demand new thread. Or opposite, you may experience thread starvation without specifying it.

One easy way to think about it is this: do you prefer new task to run in a new thread or you don't care? If first - then use LongRunningOption. This doesn't mean what task will run in another thread, it's just a good criteria when you have to specify it.

E.g. when using ContinueWith then LongRunning is opposite to ExecuteSynchronously (there is a check to prevent both being specified). If you have multiple continuations, then maybe you want to avoid overhead of queue and run specific continuation in same thread or opposite - you don't want one of continuations to interfere with others and then you can specifically use LongRunning. Refer to this article (and this) to learn about ExecuteSynchronously.