48

a code like below will start a new thread to do the job. Is there any way I can control the priority of that thread?

Task.Factory.StartNew(() => {
    // everything here will be executed in a new thread.
    // I want to set the priority of this thread to BelowNormal
});
John Saunders
  • 160,644
  • 26
  • 247
  • 397
Moon
  • 33,439
  • 20
  • 81
  • 132

4 Answers4

62

As others have mentioned, you need to specify a custom scheduler to go with your task. Unfortunately there isn't a suitable built-in scheduler.

You could go for the ParallelExtensionsExtras that Glenn linked to, but if you want something simple that can just be pasted right into your code, try the following. Use like this:

Task.Factory.StartNew(() => {
    // everything here will be executed in a thread whose priority is BelowNormal
}, null, TaskCreationOptions.None, PriorityScheduler.BelowNormal);

The code:

public class PriorityScheduler : TaskScheduler
{
    public static PriorityScheduler AboveNormal = new PriorityScheduler(ThreadPriority.AboveNormal);
    public static PriorityScheduler BelowNormal = new PriorityScheduler(ThreadPriority.BelowNormal);
    public static PriorityScheduler Lowest = new PriorityScheduler(ThreadPriority.Lowest);

    private BlockingCollection<Task> _tasks = new BlockingCollection<Task>();
    private Thread[] _threads;
    private ThreadPriority _priority;
    private readonly int _maximumConcurrencyLevel = Math.Max(1, Environment.ProcessorCount);

    public PriorityScheduler(ThreadPriority priority)
    {
        _priority = priority;
    }

    public override int MaximumConcurrencyLevel
    {
        get { return _maximumConcurrencyLevel; }
    }

    protected override IEnumerable<Task> GetScheduledTasks()
    {
        return _tasks;
    }

    protected override void QueueTask(Task task)
    {
        _tasks.Add(task);

        if (_threads == null)
        {
            _threads = new Thread[_maximumConcurrencyLevel];
            for (int i = 0; i < _threads.Length; i++)
            {
                int local = i;
                _threads[i] = new Thread(() =>
                {
                    foreach (Task t in _tasks.GetConsumingEnumerable())
                        base.TryExecuteTask(t);
                });
                _threads[i].Name = $"PriorityScheduler: {i}";
                _threads[i].Priority = _priority;
                _threads[i].IsBackground = true;
                _threads[i].Start();
            }
        }
    }

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        return false; // we might not want to execute task that should schedule as high or low priority inline
    }
}

Notes:

  • the worker threads are all background threads, so important tasks should not be scheduled using this scheduler; only those which can be discarded if the process shuts down
  • adapted from an implementation by Bnaya Eshet
  • I don't fully understand every override; just going with Bnaya's choices for MaximumConcurrencyLevel, GetScheduledTasks and TryExecuteTaskInline.
Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • 7
    It appears this now has an (uncredited) place on GitHub, with the license changed (probably not legal) and some additions for coping with finalization/disposal, though perhaps not the AppDomain itself going down (IRegisteredObject). – Chris Moschini Mar 06 '14 at 18:35
  • 2
    How wonderfully verbose if you just want to make use of the easiness of the TaskParallelLibrary AND set a priority... No offense though. Thanks for the answer. – Mike de Klerk Nov 20 '15 at 08:28
  • This solution appears to work, but deadlocks under a heavy load. – Quark Soup Nov 02 '20 at 16:01
  • @Quarkly All the concurrency is handled by `BlockingCollection` and there's no fancy multi-threading here. Are you sure it's this code that's deadlocking and not your tasks? – Roman Starkov Nov 02 '20 at 18:04
  • I've got 500 threads blocking on the call to **_tasks.GetConsumingEnumerable()**. If I replace the **PriorityScheduler.Lowest** with **TaskScheduler.Default**, my security price feed simulator works just fine (except that it's a little too aggressive about taking CPU time). – Quark Soup Nov 02 '20 at 18:28
  • dead link "an implementation by Bnaya Eshet" – Jens Dec 01 '21 at 17:48
20

Thread priority for Tasks can be set inside the actual method that executes the Task. But don't forget to restore the priority once you are done to avoid problems.

So first start the Task:

new TaskFactory().StartNew(StartTaskMethod);

Then set the thread priority:

void StartTaskMethod()
{
    try
    {
        // Change the thread priority to the one required.
        Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;

        // Execute the task logic.
        DoSomething();
    }
    finally
    {
        // Restore the thread default priority.
        Thread.CurrentThread.Priority = ThreadPriority.Normal;
    }
}

When changing the priority, keep in mind this: Why *not* change the priority of a ThreadPool (or Task) thread?

Community
  • 1
  • 1
net_prog
  • 9,921
  • 16
  • 55
  • 70
  • 10
    This approach seems dangerous to me. Isn't that setting the priority on one of the ThreadPool threads? You don't know where that thread will be used next. – Brannon Feb 19 '13 at 23:26
  • @Brannon, changing the priority via the Task Scheduler will have a similar effect I suppose. In general you don't change the priority at all, but if you require doing this, there is no difference how you do. – net_prog Apr 19 '13 at 14:08
  • 2
    @net_prog, I think you don't understand Brannon point. A thread from a thread pool is made to be re-used. The next time you will want to re-use a thread, you will have to ensure proper priority. Default behavior is to have thread in normal priority. I you use a third party lib that uses the thread pool, you can falls into troubles if the priority are not as expected. – Eric Ouellet Dec 11 '13 at 19:13
  • This is a bad idea because these same ThreadPool threads are used by ASP.Net itself to service Requests. If you had code that regularly set ThreadPriority, and it sometimes threw an exception before your code set priority back to Normal (or no code to do so, as you have here), you'd end up stranding all ASP.Net requests or locking up the server, depending on what you did. DON'T DO THIS! Use a separate PriorityScheduler like the top-voted answer here recommends. – Chris Moschini Mar 06 '14 at 18:41
  • 5
    (The comments above are now mostly outdated as the code has since been updated to use a try..finally. However it could be further improved to capture the initial thread priority instead of setting the value back to Normal.) – user2864740 Jan 22 '16 at 06:39
  • 1
    As a Thread could execute different Tasks, is this possible this Thread being preempted to execute another Task and then having a higher priority with no reason ? – Elo Jan 23 '20 at 16:18
12

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

zerkms
  • 249,484
  • 69
  • 436
  • 539
  • 6
    Yes I know about Thread.Priority. But I just wanted to know if it was possible with Task Factory instead of using objects of Thread. – Moon Oct 01 '10 at 04:59
  • At the first link it is description that said that it is not possible with Task Factory. Anyway I never need to change priority for threads from pool and not 100% sure that it is truth. – zerkms Oct 01 '10 at 05:00
  • I initially selected @Roman Starkov answer, but after load testing, it failed. I've come around to this answer: **If you want to change the priority, use a Thread. Don't use a Task** – Quark Soup Nov 03 '20 at 01:29
8

For setting priority with Task, check out the custom task schedulers described by Microsoft expert Stephen Toub in this MSDN blog post. For further detail, don't miss the links to the previous two posts that he mentions in the first sentence.

For your issue, it sounds like you might want to look at the QueuedTaskScheduler.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
  • Agree with @GlennSlayden, you should use a Custom TaskScheduler. I use the source code provider by Microsoft here : https://learn.microsoft.com/fr-fr/dotnet/api/system.threading.tasks.taskscheduler?view=netframework-4.8. This code uses the ThreadPool to execute Tasks, but you can handle a custom dedicated Thread with different prority instead. – Elo Jan 23 '20 at 16:20