创建大量任务/线程并等待它们全部完成

时间:2016-07-25 15:28:03

标签: c# multithreading task

我正在编写一个相当简单的Raytracer,但由于程序是单线程的,因此我遇到了运行时限制。我通过谷歌找到的结果都回答了这类问题,需要处理两到三个任务。

class Program
{
    static void Main(string[] args)
    {
        var taskList = new List<Task>();

        taskList.Add(Task.Factory.StartNew(() => doStuff()));
        taskList.Add(Task.Factory.StartNew(() => doStuff()));
        taskList.Add(Task.Factory.StartNew(() => doStuff()));

        Task.WaitAll(taskList);

        Console.WriteLine("All threads complete");
    }

    static void doStuff()
    {
        //do stuff here
    }
}

如果天真地实施,我会查看至少10,000个单独的线程。上面的解决方案似乎不是这种情况下的最佳解决方案。是否有标准库的一部分支持这个,或者是否有一个类似于此实现的Nuget包?它也可能只是我的愚蠢,而List中的&gt; 10,000个线程根本不是问题。然后问题变为截止时间。在某些情况下我需要12500000个任务/线程,我很确定这对于列表来说太多了。

以下是我现在要创建新线程/任务的方法。

for (var x = 0; x < image.Width; x++) {
    for (var y = 0; y < image.Height; y++) {
        var coordinates = new Vector3(x, y, 0);
        var task = new Task(() => {
            RenderSinglePixel(coordinates);
        });
    }
}

2 个答案:

答案 0 :(得分:2)

如果您有要使用多个线程处理的值的列表(或其他IEnumerable<T>),则可以使用.AsParallel()来执行此操作。

这会智能地限制同时生成的线程数,具体取决于处理器功能。但请注意,只有当每件商品的工作量相对较大时才应使用此功能。

以下是一个例子:

using System;
using System.Linq;
using System.Threading;

namespace Demo
{
    class Program
    {
        static void Main()
        {
            var numbersToProcess = Enumerable.Range(1, 1000);

            numbersToProcess.AsParallel().ForAll(doStuff);
        }

        static void doStuff(int value)
        {
            Console.WriteLine("Thread {0} is processing {1}", Thread.CurrentThread.ManagedThreadId, value);
            Thread.Sleep(250); // Simulate compute-bound task.
        }
    }
}

另一种方法是为每个方法调用创建任务,但是除非您存储任务以便等待它们完成,否则更难以知道所有线程何时完成(但线程池使用将确保线程数不会太大):

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    class Program
    {
        static void Main()
        {
            var numbersToProcess = Enumerable.Range(1, 1000);

            foreach (int number in numbersToProcess)
            {
                int n = number;
                Task.Run(() => doStuff(n));
            }

            Console.ReadLine();
        }

        static void doStuff(int value)
        {
            Console.WriteLine("Thread {0} is processing {1}", Thread.CurrentThread.ManagedThreadId, value);
            Thread.Sleep(250); // Simulate compute-bound task.
        }
    }
}

注意如果每次调用doStuff()需要很长时间,这种方法确实会冒着创建失控线程数的风险。如果您将Thread.Sleep(250)更改为Thread.Sleep(100000)并运行该程序,您将看到创建了大量线程。

但你最好的选择可能是使用the DataFlow TPL

答案 1 :(得分:1)

对小体图案使用平行环。 https://msdn.microsoft.com/en-us/library/dd560853(v=vs.110).aspx

  

当Parallel.For循环具有较小的主体时,它的执行速度可能比等效的顺序循环慢,例如C#中的for循环和Visual Basic中的For循环。较慢的性能是由分区数据所涉及的开销和每次循环迭代调用委托的成本引起的。为了解决这种情况,Partitioner类提供了Partitioner.Create方法,该方法使您能够为委托主体提供顺序循环,以便每个分区仅调用一次委托,而不是每次迭代调用一次。

Small Bodies的并行循环模式实质上是对可枚举的分区,并根据处理器的数量在多个线程中执行循环。每个线程都有自己的分区组来处理。

在这种情况下,这种模式比普通的并行循环更好(性能更高),因为它避免了创建超过必要的线程的开销。

使用超过CPU内核的线程只会降低处理的整体速度