降低 Task.Factory.StartNew 线程的优先级

Posted

技术标签:

【中文标题】降低 Task.Factory.StartNew 线程的优先级【英文标题】:lowering priority of Task.Factory.StartNew thread 【发布时间】:2011-04-19 16:36:26 【问题描述】:

下面的代码将启动一个新线程来完成这项工作。有什么办法可以控制该线程的优先级?

Task.Factory.StartNew(() => 
    // everything here will be executed in a new thread.
    // I want to set the priority of this thread to BelowNormal
);

【问题讨论】:

【参考方案1】:

正如其他人所提到的,您需要指定一个自定义调度程序来处理您的任务。不幸的是,没有合适的内置调度程序。

您可以选择 Glenn 链接到的 ParallelExtensionsExtras,但如果您想要一些可以直接粘贴到代码中的简单内容,请尝试以下操作。像这样使用:

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

代码:

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
    

注意事项:

工作线程都是后台线程,所以不应该使用这个调度器来调度重要的任务;仅当进程关闭时可以丢弃的那些 改编自an implementation by Bnaya Eshet 我不完全理解每一个覆盖;只需选择 Bnaya 的 MaximumConcurrencyLevelGetScheduledTasksTryExecuteTaskInline

【讨论】:

现在看来,这在 GitHub 上有一个(未经授权的)位置,许可证已更改(可能不合法)和一些用于处理最终确定/处置的附加内容,尽管 AppDomain 本身可能不会关闭(IRegisteredObject )。 如果您只是想利用 TaskParallelLibrary 的易用性并设置优先级,那么这将是多么的冗长......不过没有冒犯。感谢您的回答。 此解决方案似乎有效,但在负载过重时会出现死锁。 @Quarkly 所有并发都由BlockingCollection&lt;T&gt; 处理,这里没有花哨的多线程。你确定是这段代码死锁了,而不是你的任务吗? 我有 500 个线程阻塞了对 _tasks.GetConsumingEnumerable() 的调用。如果我将 PriorityScheduler.Lowest 替换为 TaskScheduler.Default,我的安全价格馈送模拟器就可以正常工作(除了占用 CPU 时间有点过于激进)。跨度> 【参考方案2】:

任务的线程优先级可以在执行任务的实际方法中设置。但是不要忘记在完成后恢复优先级以避免出现问题。

所以首先启动任务:

new TaskFactory().StartNew(StartTaskMethod);

然后设置线程优先级:

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;
    

更改优先级时,请记住:Why *not* change the priority of a ThreadPool (or Task) thread?

【讨论】:

这种方法对我来说似乎很危险。这不是在 ThreadPool 线程之一上设置优先级吗?你不知道接下来会在哪里使用该线程。 @Brannon,我想通过任务计划程序更改优先级会产生类似的效果。一般来说,你根本不会改变优先级,但如果你需要这样做,你怎么做也没什么区别。 @net_prog,我想你不明白布兰农的观点。来自线程池的线程被重新使用。下次您想要重用线程时,您必须确保适当的优先级。默认行为是让线程处于正常优先级。如果您使用使用线程池的第三方库,如果优先级不符合预期,您可能会遇到麻烦。 (上面的 cmets 现在大部分已经过时了,因为代码已经更新为使用 try..finally。但是它可以进一步改进以捕获初始线程优先级,而不是将值设置回正常。) 由于一个线程可以执行不同的任务,这可能是这个线程被抢占执行另一个任务然后无缘无故地获得更高的优先级吗?【参考方案3】:

当您决定是否使用线程池时,这是“不做”之一;-)

更多详情:http://msdn.microsoft.com/en-us/library/0ka9477y.aspx

所以答案是“不,您不能为在 Theads Pool 中创建的线程指定特定优先级”

关于一般线程,我敢打赌你已经知道Thread.Priority 属性

【讨论】:

是的,我知道 Thread.Priority。但我只是想知道是否可以使用任务工厂而不是使用线程对象。 在第一个链接的描述中说任务工厂是不可能的。无论如何,我永远不需要更改池中线程的优先级,也不是 100% 确定这是真的。 我最初选择了@Roman Starkov 答案,但经过负载测试,它失败了。我得到了这个答案:如果您想更改优先级,请使用线程。不要使用任务【参考方案4】:

要使用Task 设置优先级,请查看微软专家Stephen Toub 在this MSDN blog post 中描述的自定义任务计划程序。有关详细信息,请不要错过他在第一句话中提到的前两篇文章的链接。

对于您的问题,听起来您可能想查看QueuedTaskScheduler

【讨论】:

同意@GlennSlayden,您应该使用自定义任务调度器。我在这里使用 Microsoft 的源代码提供程序:docs.microsoft.com/fr-fr/dotnet/api/…。此代码使用 ThreadPool 执行任务,但您可以处理具有不同优先级的自定义专用线程。

以上是关于降低 Task.Factory.StartNew 线程的优先级的主要内容,如果未能解决你的问题,请参考以下文章

Task.Run 和Task.Factory.StartNew 区别

Task.Factory.StartNew 测试

Task.Factory.StartNew 和 Task.Run 到底有什么区别?

Task.Factory.StartNew的用法

Task.Run() 和 Task.Factory.StartNew() 有啥区别

Task.Run 和 Task.Factory.StartNew 区别