Почему так много различий в производительности между Tasks, Thread и ThreadPool? - PullRequest
0 голосов
/ 30 декабря 2011

Здесь я приложил мой пример, который я использовал для теста производительности.Почему между всем этим так много различий?(Это пример консольного приложения)

class Program
    {
    internal class ThreadObj
        {
        public ManualResetEvent signalComplete { get; set; }
        public int TaskItem { get; set; }
        }

    static void ThreadWork(object o)
        {
        ThreadObj obj = (ThreadObj)o;           
        System.Threading.Thread.Sleep(5000);            
        obj.signalComplete.Set();
        }
    static void Main(string[] args)
        {
        // Using new .net 4.0 Task
        Stopwatch watch = new Stopwatch();
        watch.Start();
        System.Collections.Concurrent.ConcurrentBag<Task> tasks = new System.Collections.Concurrent.ConcurrentBag<Task>();
        Parallel.For(0, 60, i =>
        {
            Task t = Task.Factory.StartNew(() =>
            {                    
                System.Threading.Thread.Sleep(5000);                    
            }, TaskCreationOptions.PreferFairness);
            tasks.Add(t);
        });
        Console.WriteLine("Waiting for task to finish");
        Task.WaitAll(tasks.ToArray());
        watch.Stop();
        Console.WriteLine("Complete(Tasks) : Time " +  watch.ElapsedMilliseconds.ToString());        



        // Using Thread 
        watch.Reset();
        watch.Start();
        System.Collections.Concurrent.ConcurrentBag<ManualResetEvent> tasksThreads = new System.Collections.Concurrent.ConcurrentBag<ManualResetEvent>();
        Parallel.For(0, 60, i =>
        {
            ManualResetEvent signal = new ManualResetEvent(false);
            tasksThreads.Add(signal);
            Thread t = new Thread(new ParameterizedThreadStart(ThreadWork));
            t.Start(new ThreadObj() { signalComplete = signal, TaskItem = i });
        });
        Console.WriteLine("Waiting for task to finish");
        WaitHandle.WaitAll(tasksThreads.ToArray());
        watch.Stop();
        Console.WriteLine("Complete(Threads) : Time " + watch.ElapsedMilliseconds.ToString());


        // Using ThreadPool
        watch.Reset();
        watch.Start();
        System.Collections.Concurrent.ConcurrentBag<ManualResetEvent> tasksThreadPools = new System.Collections.Concurrent.ConcurrentBag<ManualResetEvent>();
        Parallel.For(0, 60, i =>
        {
            ManualResetEvent signal = new ManualResetEvent(false);
            tasksThreadPools.Add(signal);
            ThreadObj obj = new ThreadObj() { signalComplete = signal, TaskItem = i };
            ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadWork), obj);
        });
        Console.WriteLine("Waiting for task to finish");
        WaitHandle.WaitAll(tasksThreadPools.ToArray());
        watch.Stop();
        Console.WriteLine("Complete(ThreadPool) : Time " + watch.ElapsedMilliseconds.ToString());
        Console.ReadLine();
        }

    }

Пожалуйста, укажите свое предложение по этому вопросу.

Вот пример вывода, который я получил.

Waiting for task to finish
Complete(Tasks) : Time 28050
Waiting for task to finish
Complete(Threads) : Time 5435
Waiting for task to finish
Complete(ThreadPool) : Time 15032

1 Ответ

2 голосов
/ 30 декабря 2011

Ваш тестовый пример далек от основания. Когда вы выполняете фактическую вычислительную работу в методе threadWork, вы обнаружите, что результаты сильно отличаются. TPL использует пул потоков внутри, так что это вопрос Threadpool против потоков. Причина, по которой TPL настолько отличается по сравнению с потоком потоков, вероятно, связана с природой самого пула потоков (вернемся к этому позже).

Посмотрите, сколько времени потребовалось на завершение темы. Ваш метод тестирования только спит в течение 5 секунд, вот и все. Куда делась другая .43 секунда? Правильно, к созданию и уничтожению самого потока и связанных с ним издержек, включая переключение контекста. Пул потоков имеет очередь потоков, которые можно использовать для одновременного выполнения. Все зависит от Threadpool и его конфигурации, позволяющей создавать и уничтожать дополнительные потоки в любое время. Когда вы запланируете 60 элементов в Пуле потоков, скорее всего, Пул потоков не создаст 60 потоков, чтобы обрабатывать все элементы одновременно, а вместо этого использовать часть этого количества и обрабатывать несколько элементов на поток. Поскольку ваш метод тестирования только спит, это объясняет большую разницу между временем, потраченным на потоки, и временем, потраченным на пул потоков.

Поскольку TPL использует Threadpool внутренне, и до того, как вы запустите тест ThreadPool, логично предположить, что на этом этапе: в Threadpool было доступно меньше потоков, но из-за запуска TPL было создано больше потоков для Threadpool, поэтому, в свою очередь, при выполнении теста Threadpool изначально было доступно больше потоков, что объясняет разницу между TPL и Threadpool.

Практически, вы хотите максимально использовать Threadpool, особенно для вычислительных операций. Когда вам нужно синхронизироваться с внешним ресурсом, таким как загрузка чего-либо из Интернета, я рекомендую не использовать поток, а один из более сложных асинхронных вариантов, доступных в .NET для получения этого конкретного ресурса.

...