简体   繁体   English

PLINQ AsParallel() 优先级较低?

[英]PLINQ AsParallel() with lower priority?

is it possible to run some of my PLINQ AsParallel() - Queries with a lower priority than others?是否可以运行我的一些 PLINQ AsParallel() - 优先级低于其他查询的查询? (Or some with a higher priority than others) Is this possible with PLinq or will I have to avoid PLINQ and do all the stuff on my own? (或者某些优先级高于其他人)PLinq 是否可以做到这一点,还是我必须避免使用 PLINQ 并自己完成所有工作?

EDIT/UPDATE:编辑/更新:

Would it be possible to call可以打电话吗

Thread.Sleep(0)

inside the parallel executed method when I want to archive a lower priority?当我想归档较低优先级时,在并行执行的方法中? Or is that a very bad practice/hack?或者这是一个非常糟糕的做法/黑客?

Unfortunately, this is not directly possible in PLINQ.不幸的是,这在 PLINQ 中无法直接实现。

You can do it in most of the rest of the Task Parallel Library via creation of a custom TaskScheduler .您可以通过创建自定义TaskScheduler在任务并行库的大部分其余部分中执行此操作。 This would allow you to have custom "priorities" when using Parallel.For or ForEach .这将允许您在使用Parallel.ForForEach时拥有自定义“优先级”。

However, the ability to customize the TaskScheduler was not provided with PLINQ, since PLINQ requires very strict guarantees from the TaskScheduler, and the fear was that exposing this would be very problematic.但是,PLINQ 没有提供自定义 TaskScheduler 的能力,因为 PLINQ 要求 TaskScheduler 提供非常严格的保证,并且担心暴露这一点会很成问题。


EDIT/UPDATE:编辑/更新:

Would it be possible to call可以打电话吗

Thread.Sleep(0)线程.睡眠(0)

This would "lower" the priority, but unfortunately, has it's own issues, especially when combined with PLINQ.这会“降低”优先级,但不幸的是,它有自己的问题,尤其是与 PLINQ 结合使用时。 This will potentially cause thread starvation in the ThreadPool, since you'll be "sleeping" on ThreadPool threads.这可能会导致 ThreadPool 中的线程饥饿,因为您将在 ThreadPool 线程上“休眠”。

In addition, there's a fundamental problem with this - PLINQ is designed and intended to handle queries , and is not designed for processing .此外,这还有一个根本问题——PLINQ 是为处理查询而设计的,而不是为处理而设计的。 Introducing logical code to control the flow structure is really against the theory behind PLINQ, and will likely cause unintended performance repercussions you aren't expecting, especially if you're using the default partitioning.引入逻辑代码来控制流结构确实违反了 PLINQ 背后的理论,并且可能会导致您意想不到的性能影响,尤其是在您使用默认分区时。

AsParallel is very high level API. AsParallel是非常高级的 API。 You should really use Thread s if you want fine grained control over what is happening using Priority如果您想使用Priority对正在发生的事情进行细粒度控制,您应该真正使用Thread s

The PLINQ library does not allow configuring the TaskScheduler , for good reasons : PLINQ 库不允许配置TaskScheduler原因如下

The reason we didn't make this public initially and then haven't each time the question has come up is due to fear of deadlocks.我们最初没有公开这一点,然后每次出现问题时都没有公开的原因是因为害怕死锁。 Some PLINQ operators use barriers, such that all tasks/partitions involved in processing the query need to join before any of them can make further progress.一些 PLINQ 运算符使用屏障,这样处理查询所涉及的所有任务/分区都需要加入,然后它们中的任何一个才能取得进一步的进展。 If the underlying scheduler can't guarantee that all of the tasks queued as part of PLINQ's processing will be able to run at the same time, eg if it's a scheduler created with a fixed number of threads, then we risk deadlock.如果底层调度程序不能保证作为 PLINQ 处理的一部分排队的所有任务都能够同时运行,例如,如果它是使用固定数量的线程创建的调度程序,那么我们就有死锁的风险。

If you feel adventurous you could consider doing this configuration with reflection.如果您喜欢冒险,可以考虑使用反射进行此配置。 Here is a PLINQ operator that does exactly this:这是一个 PLINQ 运算符,它正是这样做的:

/// <summary>
/// Sets the scheduler that should execute the query.
/// </summary>
/// <remarks>Has been tested on .NET 6 only.</remarks>
public static ParallelQuery<T> WithScheduler<T>(this ParallelQuery<T> source,
    TaskScheduler scheduler)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (scheduler == null) throw new ArgumentNullException(nameof(scheduler));
    const string name1 = "_specifiedSettings";
    FieldInfo fi1 = typeof(ParallelQuery).GetField(name1,
        BindingFlags.NonPublic | BindingFlags.Instance);
    if (fi1 == null) throw new InvalidOperationException($"Field {name1} not found.");
    object querySettings = fi1.GetValue(source);
    if (querySettings == null) throw new InvalidOperationException($"{name1} is null.");
    const string name2 = "TaskScheduler";
    PropertyInfo pi2 = querySettings.GetType().GetProperty(name2,
        BindingFlags.NonPublic | BindingFlags.Instance);
    if (pi2 == null) throw new InvalidOperationException($"Property {name2} not found.");
    pi2.SetValue(querySettings, scheduler);
    fi1.SetValue(source, querySettings); // The QuerySettings is a struct
    return source;
}

Usage example:使用示例:

TaskScheduler lowPriorityScheduler = new ConcurrentExclusiveSchedulerPair(
    TaskScheduler.Default, Environment.ProcessorCount).ConcurrentScheduler;

var query = source
    .AsParallel()
    .WithScheduler(lowPriorityScheduler)
    //...

You could use the same lowPriorityScheduler to schedule other operations that should run with low priority, in parallel with this and other queries.您可以使用相同的lowPriorityScheduler来安排其他应以低优先级运行的操作,与此查询和其他查询并行运行。 All these operations combined will use at most Environment.ProcessorCount threads from the ThreadPool .所有这些操作结合起来将最多使用ThreadPool中的Environment.ProcessorCount个线程。 All other threads that are available beyond this limit, will be used exclusively by non-low-priority operations.超出此限制的所有其他可用线程将由非低优先级操作独占使用。

You could also take a look at this somewhat related question.你也可以看看这个有点相关的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM