如何使 Parallel.For 按顺序对其迭代进行排队

How to make Parallel.For to queue its iterations sequentialy

我有一个 for 循环。平行于。以及大量的迭代。我想利用环境中的所有处理器内核。但我真的需要迭代按顺序排队。

例如,我从 Parallel.For 到 运行 有 100 次迭代,并且有 4 个核心可用。似乎 运行 的第一次迭代是第 0、25、50、75。我想要的是他们是第一、第二、第三和第四,当其中一个完成时,下一个应该是第五,然后是第六,依此类推。

当我乱用"degree of parallelism"参数时,它只是将迭代范围划分不同,例如第0、10、20等。我可以实现顺序排队的唯一方法是将并行度设置为等于全部迭代次数,这样它们至少会按顺序开始。但我认为这不是最有效的方法,因为多余的流程会减慢之前启动的流程。

那么我如何排队 Parallel.for 次迭代以按顺序开始,但同时不超过指定的数量?

如果您需要特殊订单,您可以使用 Queue 以确保先进先出:

public static void DoSomething(int x)
{
    Console.WriteLine("Starting " + x);
    Thread.Sleep((x%10) * 1000);
}

public static void Main(string[] args)
{

    Queue<int> myList = new Queue<int>();
    for (int i = 0; i < 100; i++)
        myList.Enqueue(i);

    // This would be random if we'd use a List
    //Parallel.ForEach(myList, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, x => DoSomething(x));

    // This will get the right order. But 2 can still be faster than 1 if 2's thread is quicker. But generally you got your order.
    Parallel.For(
        0,  // We count from 0
        myList.Count,  // to max entries..
        new ParallelOptions() { MaxDegreeOfParallelism = 4 },  // don't forget this one ;)
        (x) => { lock (myList) { int y = myList.Dequeue(); Console.WriteLine(y); DoSomething(y); } });
}

查看输出。如果两个线程并行开始,则您不知道顺序。但是一般下一个就会出队

这可能有些牵强,但我只是在这里思考。充其量是对问题的不同看法,最坏的情况是投反对票。 :(

您可以先拆分值以进行操作,例如:

public Dictionary<string, List<int>> CreateDictionary()
{
    var rtnValue = new Dictionary<string, List<int>>()
    {
        { "0", new List<int>() },
        { "1", new List<int>() },
        { "2", new List<int>() },
        { "3", new List<int>() }
    };


    var rando = new Random();
    for (int i = 0; i < 100; i++)
    {
        if (i < 25)
            rtnValue["0"].Add(rando.Next(-100, 100));

        if (i > 24 && i < 50)
            rtnValue["1"].Add(rando.Next(-100, 100));

        if (i > 49 && i < 75)
            rtnValue["2"].Add(rando.Next(-100, 100));

        if (i > 74 && i < 100)
            rtnValue["3"].Add(rando.Next(-100, 100));
    }

    return rtnValue;
}

现在让我们有一个方法来完成这项工作。

public static string processList(IList<int> param)
{
    return string.Join(", ", param);
}

最后像这样并行完成工作:

public void runInParallel()
{
    var DataToOperateOn = CreateDictionary();
    Parallel.For(0, DataToOperateOn.Count, i =>
    {
        processList(DataToOperateOn[i.ToString()]);
    });
}

或者,您可以使用 Parallel.Invoke 完成大致相同的事情,例如:

public void runInParallel()
{
    var DataToOperateOn = CreateDictionary();
    Parallel.Invoke(
    () => { processList(DataToOperateOn["0"]); },
    () => { processList(DataToOperateOn["1"]); },
    () => { processList(DataToOperateOn["2"]); },
    () => { processList(DataToOperateOn["3"]); });
}