简体   繁体   English

防止并行操作在顺序批处理中执行

[英]Prevent Parallel operation executing in sequential batches

I have 8 logical processors.我有 8 个逻辑处理器。 When executing the following code执行以下代码时

public void test()
{
    Parallel.For(1, 1001, i => { IntensiveWork(i); });
}

private static void IntensiveWork(int i)
{
    Random r = new Random();
    Thread.Sleep(r.Next(i * 1));
}

I notice that the Parallel.For makes multiple batches of 8 jobs.我注意到Parallel.For进行了多批 8 个作业。 Each batch will be executed sequentially.每批将按顺序执行。 The issue in this is that if 7/8 jobs in the batch finished, then the next batch will keep waiting for the last job to finish.这里的问题是,如果批次中有 7/8 个作业完成,那么下一个批次将继续等待最后一个作业完成。 This means that 7 cores will not be busy.这意味着7个核心不会忙。 Is there a better way to implement parallelism is C#, in which once a job in the batch finishes it will assign that core another job.有没有更好的方法来实现并行性是 C#,其中一旦批处理中的作业完成,它将为该核心分配另一个作业。

You can make single queue that multiple tasks will read from. 您可以将多个任务从中读取单个队列。

static void test()
{
    ConcurrentQueue<int> queue = new ConcurrentQueue<int>(Enumerable.Range(1, 1000));
    int taskCount = Environment.ProcessorCount;
    Task[] tasks = new Task[taskCount];
    for (int taskIndex = 0; taskIndex < taskCount; taskIndex++)
    {
        Task task = Task.Factory.StartNew(() => IntensiveWorkTask(queue));
        tasks[taskIndex] = task;
    }
    Task.WaitAll(tasks);
}

private static void IntensiveWorkTask(ConcurrentQueue<int> queue)
{
    while (queue.TryDequeue(out int value))
        IntensiveWork(value);
}

private static void IntensiveWork(int i)
{
    Random r = new Random();
    Thread.Sleep(r.Next(i * 1));
}

Try Microsoft's Reactive Framework (aka Rx) - just NuGet System.Reactive and then add using System.Reactive.Linq; 尝试使用Microsoft的Reactive Framework(aka Rx)-仅NuGet System.Reactive ,然后using System.Reactive.Linq;添加using System.Reactive.Linq; - then you can do this: -那么您可以执行以下操作:

public void test()
{
    IObservable<Unit> query =
        Observable
            .Range(1, 1000)
            .SelectMany(i =>
                Observable
                    .Start(() => IntensiveWork(i)));

    IDisposable subscription = query.Subscribe();
}

private static Random r = new Random();

private static void IntensiveWork(int i)
{
    Thread.Sleep(r.Next(i * 1));
}

Play with the .Subscribe(... to be able to respond to each work item when it is completed. 播放.Subscribe(... ,以便能够在完成每个工作项目时对其进行响应。

Instead of:代替:

Parallel.For(1, 1001, parallelOptions, i => IntensiveWork(i));

You should do:你应该做:

IEnumerable<int> source = Enumerable.Range(1, 1000);

Partitioner<int> partitioner = Partitioner
    .Create(source, EnumerablePartitionerOptions.NoBuffering);
    
Parallel.ForEach(partitioner, parallelOptions, i => IntensiveWork(i));

The partitioning strategy of the Parallel.For is a combination of range partitioning and chunk partitioning, which is not appropriate for your case, and cannot be configured. Parallel.For分区策略是范围分区和块分区的组合,不适合您的情况,无法配置。 What you want is a partitioner with load balancing behavior, with chunk partitioning disabled.你想要的是一个具有负载平衡行为的分区器,禁用块分区。 That's why you have to switch to Parallel.ForEach , and configure the partitioner with the EnumerablePartitionerOptions.NoBuffering option.这就是为什么您必须切换到Parallel.ForEach并使用EnumerablePartitionerOptions.NoBuffering选项配置分区程序的原因。

The parallelOptions is shown below. parallelOptions如下所示。 It's always a good idea to specify explicitly the MaxDegreeOfParallelism , for example to Environment.ProcessorCount , instead of relying on the default -1 (unlimited) configuration that saturates the ThreadPool .明确指定MaxDegreeOfParallelism始终是一个好主意,例如Environment.ProcessorCount ,而不是依赖使ThreadPool饱和的默认-1 (无限制)配置。

ParallelOptions parallelOptions = new()
{
    MaxDegreeOfParallelism = Environment.ProcessorCount
};

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

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