从IEnumerable Range for Parallel For循环中选择相等的“块”

时间:2018-02-17 22:50:29

标签: c# multithreading parallel-processing

这是一个由两部分组成的问题。

我已经以编程方式确定了一系列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)获得。理想情况下,我最终会得到像{{1}这样的东西} List,其中每个Tuples包含每个块的起始值和结束值:

Tuple

1)如何在较少的代码中选择内部范围,而不必知道我需要多少元组?我已经想出了一个很长的方法来做这个循环,但我希望LINQ可以在这里提供帮助:

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

一旦我有了这个新的范围元组列表,我想将这些作为并行循环的基础,在某些条件下写入 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()) }; 。我不知道如何将这些代码放入我的循环中......

我已经将这段代码用于多个线程,但 2)如何根据ConcurrentDictionary 中定义的范围在所有处理器之间均匀分配工作:

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;
                             }
                         }
                     });

Thread1需要处理第一个元组中的范围,thread2处理第二个元组中定义的范围等,其中... 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 ... 由循环中的范围定义。同样,范围元组的数量将取决于处理器的数量。感谢。

1 个答案:

答案 0 :(得分:1)

  

我想根据处理器数量将这个数字列表分成几个块

LINQ Batch方法有many possible implementations

  

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

这是处理这个问题的一种方法:

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需要处理第一个元组中的范围,thread2处理第二个元组中定义的范围等...

除非您对某些线程处理某些批次有一些硬性要求,否则我强烈建议将您的工作生成为单个“流”,并使用更高级别的抽象来实现并行化。 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
...
*/