Lowering Priority of Task.Factory.Startnew Thread

lowering priority of Task.Factory.StartNew thread

This is one of "not to do" when you decide whether to use thread pool or not ;-)

More details here: http://msdn.microsoft.com/en-us/library/0ka9477y.aspx

So the answer is "No, you cannot specify particular priority for thread created in Theads Pool"

As of general threadings I bet you already know about Thread.Priority property

Is Task.Factory.StartNew() guaranteed to use another thread than the calling thread?

I mailed Stephen Toub - a member of the PFX Team - about this question. He's come back to me really quickly, with a lot of detail - so I'll just copy and paste his text here. I haven't quoted it all, as reading a large amount of quoted text ends up getting less comfortable than vanilla black-on-white, but really, this is Stephen - I don't know this much stuff :) I've made this answer community wiki to reflect that all the goodness below isn't really my content:

If you call Wait() on a Task that's completed, there won't be any blocking (it'll just throw an exception if the task completed with a TaskStatus other than RanToCompletion, or otherwise return as a nop). If you call Wait() on a Task that's already executing, it must block as there’s nothing else it can reasonably do (when I say block, I'm including both true kernel-based waiting and spinning, as it'll typically do a mixture of both). Similarly, if you call Wait() on a Task that has the Created or WaitingForActivation status, it’ll block until the task has completed. None of those is the interesting case being discussed.

The interesting case is when you call Wait() on a Task in the WaitingToRun state, meaning that it’s previously been queued to a TaskScheduler but that TaskScheduler hasn't yet gotten around to actually running the Task's delegate yet. In that case, the call to Wait will ask the scheduler whether it's ok to run the Task then-and-there on the current thread, via a call to the scheduler's TryExecuteTaskInline method. This is called inlining. The scheduler can choose to either inline the task via a call to base.TryExecuteTask, or it can return 'false' to indicate that it is not executing the task (often this is done with logic like...

return SomeSchedulerSpecificCondition() ? false : TryExecuteTask(task);

The reason TryExecuteTask returns a Boolean is that it handles the synchronization to ensure a given Task is only ever executed once). So, if a scheduler wants to completely prohibit inlining of the Task during Wait, it can just be implemented as return false; If a scheduler wants to always allow inlining whenever possible, it can just be implemented as:

return TryExecuteTask(task);

In the current implementation (both .NET 4 and .NET 4.5, and I don’t personally expect this to change), the default scheduler that targets the ThreadPool allows for inlining if the current thread is a ThreadPool thread and if that thread was the one to have previously queued the task.

Note that there isn't arbitrary reentrancy here, in that the default scheduler won’t pump arbitrary threads when waiting for a task... it'll only allow that task to be inlined, and of course any inlining that task in turn decides to do. Also note that Wait won’t even ask the scheduler in certain conditions, instead preferring to block. For example, if you pass in a cancelable CancellationToken, or if you pass in a non-infinite timeout, it won’t try to inline because it could take an arbitrarily long amount of time to inline the task's execution, which is all or nothing, and that could end up significantly delaying the cancellation request or timeout. Overall, TPL tries to strike a decent balance here between wasting the thread that’s doing the Wait'ing and reusing that thread for too much. This kind of inlining is really important for recursive divide-and-conquer problems (e.g. QuickSort) where you spawn multiple tasks and then wait for them all to complete. If such were done without inlining, you’d very quickly deadlock as you exhaust all threads in the pool and any future ones it wanted to give to you.

Separate from Wait, it’s also (remotely) possible that the Task.Factory.StartNew call could end up executing the task then and there, iff the scheduler being used chose to run the task synchronously as part of the QueueTask call. None of the schedulers built into .NET will ever do this, and I personally think it would be a bad design for scheduler, but it’s theoretically possible, e.g.:

protected override void QueueTask(Task task, bool wasPreviouslyQueued)
{
return TryExecuteTask(task);
}

The overload of Task.Factory.StartNew that doesn’t accept a TaskScheduler uses the scheduler from the TaskFactory, which in the case of Task.Factory targets TaskScheduler.Current. This means if you call Task.Factory.StartNew from within a Task queued to this mythical RunSynchronouslyTaskScheduler, it would also queue to RunSynchronouslyTaskScheduler, resulting in the StartNew call executing the Task synchronously. If you’re at all concerned about this (e.g. you’re implementing a library and you don’t know where you’re going to be called from), you can explicitly pass TaskScheduler.Default to the StartNew call, use Task.Run (which always goes to TaskScheduler.Default), or use a TaskFactory created to target TaskScheduler.Default.


EDIT: Okay, it looks like I was completely wrong, and a thread which is currently waiting on a task can be hijacked. Here's a simpler example of this happening:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1 {
class Program {
static void Main() {
for (int i = 0; i < 10; i++)
{
Task.Factory.StartNew(Launch).Wait();
}
}

static void Launch()
{
Console.WriteLine("Launch thread: {0}",
Thread.CurrentThread.ManagedThreadId);
Task.Factory.StartNew(Nested).Wait();
}

static void Nested()
{
Console.WriteLine("Nested thread: {0}",
Thread.CurrentThread.ManagedThreadId);
}
}
}

Sample output:

Launch thread: 3
Nested thread: 3
Launch thread: 3
Nested thread: 3
Launch thread: 3
Nested thread: 3
Launch thread: 3
Nested thread: 3
Launch thread: 4
Nested thread: 4
Launch thread: 4
Nested thread: 4
Launch thread: 4
Nested thread: 4
Launch thread: 4
Nested thread: 4
Launch thread: 4
Nested thread: 4
Launch thread: 4
Nested thread: 4

As you can see, there are lots of times when the waiting thread is reused to execute the new task. This can happen even if the thread has acquired a lock. Nasty re-entrancy. I am suitably shocked and worried :(

Newly created threads using Task.Factory.StartNew starts very slowly

Found out that the thread pool can be unwilling to start more than one new thread every 500 msec when the number of thread pool threads used are over a specific value. However increasing MinThreads using ThreadPool.SetMinThreads - even though it is not recommended - to 100 enables me to create 100 threads without the 500 msec delay.

Here's what helped me:

  • http://alexpinsker.blogspot.com/2009/06/threadpool.html

  • http://msdn.microsoft.com/en-us/library/system.threading.threadpool.setminthreads%28v=vs.100%29.aspx

  • https://stackoverflow.com/a/13186389/600559

Edit:

Here's what I ended doing in App.xaml.cs (in the constructor):

// Get thread pool information
int workerThreadsMin, completionPortThreadsMin;
ThreadPool.GetMinThreads(out workerThreadsMin, out completionPortThreadsMin);
int workerThreadsMax, completionPortThreadsMax;
ThreadPool.GetMaxThreads(out workerThreadsMax, out completionPortThreadsMax);

// Adjust min threads
ThreadPool.SetMinThreads(workerThreadsMax, completionPortThreadsMin);

Why *not* change the priority of a ThreadPool (or Task) thread?

The thread pool, especially the .NET 4.0 thread pool, has many tricks up its sleeve and is a rather complicated system. Add in tasks and task schedulers and work stealing and all sorts of other things and the reality is you don't know what is going on. The thread pool may notice that your task is waiting on I/O and decide to schedule something quick on your task or suspend your thread to run something of higher priority. Your thread may somehow be a dependency for a higher-priority thread (that you may or may not be aware of) and end up causing a deadlock. Your thread may die in some abnormal way and be unable to restore priority.

If you have a long-running task such that you think it would be best that your thread have a lower priority then the thread pool probably isn't for you. While the algorithms have been improved in .NET 4.0, it is still best used for short-lived tasks where the cost of creating a new thread is disproportional to the length of the task. If your task runs for more than a second or two the cost of creating a new thread is insignificant (although management might be annoying).

Change thread priority from lowest to highest in .net

This is not a good approach. First off, LinkedList<T> is not thread safe, so writing to it and reading from it in two threads will cause race conditions.

A better approach would be to use BlockingCollection<T>. This allows you to add items (producer thread) and read items (consumer thread) without worrying about thread safety, as it's completely thread safe.

The reading thread can just call blockingCollection.GetConsumingEnumerable() in a foreach to get the elements, and the write thread just adds them. The reading thread will automatically block, so there's no need to mess with priorities.

When the writing thread is "done", you just call CompleteAdding, which will, in turn, allow the reading thread to finish automatically.

It takes more than a few seconds for a task to start running

Tasks are scheduled on the thread pool (by default). If there are lots of other tasks/thread pool usage (and especially if long running tasks are being created but not flagged as such), it can take a while for the thread pool to scale such that a thread is available to execute a newly queued item.

So, I'd look at your system as a whole and see whether you're putting too much work into the thread pool or using it inappropriately.

Can I somehow influence the task priority, so it would start running immediately in all scenarios?

Well, you can manually create threads and take over all usage, but note that even there it's not "immediate". It's as quickly as the OS chooses to schedule any newly created thread.

Or if you truly need code to run "immediately", run it on a thread that you already know is scheduled and running - your own current thread. Of course, then you lose the advantage of asking the TPL to handle the task and just get notified when it's complete. And possible tie up a precious thread such as the UI one.



Related Topics



Leave a reply



Submit