从并行 For 循环的 IE可数范围中选择相等的"chunks"



这是一个两部分的问题。

我已经编程确定了一系列double值:

    public static void Main(string[] args)
    {
        var startRate = 0.0725;
        var rateStep = 0.001;
        var maxRate = 0.2;
        var stepsFromStartToMax = (int)Math.Ceiling((maxRate-startRate)/rateStep);
        var allRateSteps = Enumerable.Range(0, stepsFromStartToMax)
            .Select(i => startRate + (maxRate - startRate) * ((double)i / (stepsFromStartToMax - 1)))
            .ToArray();
        foreach (var i in allRateSteps)
        {
            Console.WriteLine(i); //  this prints the correct values
        }
    }

我想根据处理器计数将此数字列表分为块,我可以从Environment.ProcessorCount(通常是8(中获得,理想情况下,我最终会得到类似TuplesList,其中每个Tuple都包含在其中每个块的开始和最终值:

[(0.725, 0.813), (0.815, 0.955), ...]

1(如何在较少的代码中选择内部范围,而不必知道我需要多少个元组?我想出了很长的路要但是我希望Linq可以在这里提供帮助:

        var counter = 0;
        var listOne = new List<double>();
        //...
        var listEight = new List<double>();
        foreach (var i in allRateSteps)
        {
            counter++;
            if (counter < allRateSteps.Length/8)
            {
                listOne.Add(i);
            }
            //...
            else if (counter < allRateSteps.Length/1)
            {
                listEight.Add(i);
            }
        }
        // Now that I have lists, I can get their First() and Last() to create tuples
        var tupleList = new List<Tuple<double, double>>{
            new Tuple<double, double>(listOne.First(), listOne.Last()),
            //...
            new Tuple<double, double>(listEight.First(), listEight.Last())
        };

一旦有了这个新的范围元素列表,我想将其中的每一个用作平行循环的基础,该基础在某些条件下写入ConcurrentDictionary。我不确定如何将此代码纳入我的循环...

我已经在多个线程上使用了此代码,但是 2(我如何根据我在tupleList 中定义的范围均匀分发工作。>

        var maxRateObj = new ConcurrentDictionary<string, double>();
        var startTime = DateTime.Now;
        Parallel.For(0,
                     stepsFromStartToMax,
                     new ParallelOptions
                     {
                         MaxDegreeOfParallelism = Environment.ProcessorCount
                     },
                     x =>
                     {
                        var i = (x * rateStep) + startRate;
                        Console.WriteLine("{0} : {1} : {2} ",
                                      i,
                                      DateTime.Now - startTime,
                                      Thread.CurrentThread.ManagedThreadId);
                         if (!maxRateObj.Any())
                         {
                             maxRateObj["highestRateSoFar"] = i;
                         }
                         else {
                             if (i > maxRateObj["highestRateSoFar"])
                             {
                                maxRateObj["highestRateSoFar"] = i;
                             }
                         }
                     });

此打印出来,例如:

...
0.1295 : 00:00:00.4846470 : 5 
0.0825 : 00:00:00.4846720 : 8 
0.1645 : 00:00:00.4844220 : 6 
0.0835 : 00:00:00.4847510 : 8 
...

thread1需要处理第一个元组中的范围,螺纹2处理第二个元组中定义的范围,等等……在其中 i由环路中的范围定义。同样,范围组的数量将取决于处理器的数量。谢谢。

我想根据处理器计数将此数字列表划分为块

LINQ Batch方法有许多可能的实现。

您如何在较少的代码中选择内部范围,而不必知道我需要多少个元组?

这是处理此操作的一种方法:

var batchRanges = from batch in allRateSteps.Batch(anyNumberGoesHere)
                  let first = batch.First()
                  let last = batch.Last()
                  select Tuple.Create(first, last);
(0.0725, 0.0795275590551181)
(0.0805314960629921, 0.0875590551181102)
(0.0885629921259842, 0.0955905511811024)
...

我如何根据我在tupleList中定义的范围均匀分发工作

您示例的这一部分不参考tupleList,因此很难看到所需的行为。

thread1需要处理第一个元组中的范围,螺纹2处理第二个元组中定义的范围,等等...

...

除非您有一些困难要求某些线程处理某些批次,否则我强烈建议将您的作品作为单个"流"生成,并使用更高级别的抽象来进行并行性,例如plinq。

如果您只想在批处理工作,您仍然可以做到这一点,但不在乎正在完成的工作:

static void Work(IEnumerable<int> ints) {
  var sum = ints.Sum();
  Thread.Sleep(sum);
  Console.WriteLine(ints.Sum());
}
public static void Main (string[] args) {
  var inputs = from i in Enumerable.Range(0, 100)
               select i + i;
  var batches = inputs.Batch(8);
  var tasks = from batch in batches
              select Task.Run(() => Work(batch));
  Task.WaitAll(tasks.ToArray());
}

默认的 TaskScheduler正在为您的幕后协调工作,并且它可能会超越您自己的线程方案。

也考虑这样的东西:

static int Work(IEnumerable<int> ints) {
  Console.WriteLine("Work on thread " + Thread.CurrentThread.ManagedThreadId);
  var sum = ints.Sum();
  Thread.Sleep(sum);
  return sum;
}
public static void Main (string[] args) {
  var inputs = from i in Enumerable.Range(0, 100)
               select i + i;
  var batches = inputs.Batch(8);
  var tasks = from batch in batches
              select Work(batch);
  foreach (var task in tasks.AsParallel()) {
    Console.WriteLine(task);
  }
}
/*
Work on thread 6
Work on thread 4
56
Work on thread 4
184
Work on thread 4
Work on thread 4
312
440
...
*/

最新更新