簡體   English   中英

創建大量任務/線程並等待它們全部完成

[英]Creating large amount of tasks/threads and waiting for them all to complete

我正在編寫一個相當簡單的Raytracer,由於程序是單線程的,因此遇到了運行時限制。 我在Google上找到的結果全部回答了此類問題,需要處理兩個或三個任務。

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包? 這也可能只是我的愚蠢之舉,並且列表中的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);
        });
    }
}

如果您有要使用多個線程處理的值的列表(或其他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)並運行程序,您會看到創建了大量線程。

但是最好的選擇是使用DataFlow TPL

將並行循環用於小物體模式。 https://msdn.microsoft.com/en-us/library/dd560853(v=vs.110).aspx

當Parallel.For循環的主體較小時,它的性能可能比等效的順序循環慢,例如C#中的for循環和Visual Basic中的For循環。 較慢的性能是由數據分區所涉及的開銷以及每次循環迭代調用委托的成本引起的。 為了解決這種情況,Partitioner類提供了Partitioner.Create方法,該方法使您可以為委托主體提供順序循環,以便每個分區僅調用一次委托,而不是每次迭代調用一次。

“小實體並行循環”模式實質上將您的可枚舉分區,並根據處理器數量在多個線程中執行循環。 每個線程將具有其自己的分區組以進行處理。

在這種情況下,這種模式比普通的並行循環更好(性能更高),因為它避免了創建不必要線程的開銷。

使用線程多於CPU內核只會降低處理的整體速度

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM