为什么在任务,线程和线程池之间存在如此多的性能差异?

时间:2011-12-30 06:33:54

标签: multithreading c#-4.0 threadpool task-parallel-library

这里我附上了我用于性能测试的示例。为什么这一切之间存在如此多的差异? (这是示例控制台应用程序)

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 个答案:

答案 0 :(得分:2)

你的测试用例远非一成不变。当您在threadWork方法中执行实际计算工作时,您会发现结果非常不同。 TPL在内部使用线程池,因此它是Threadpool vs Threads的问题。与Threadpool相比,TPL之所以如此不同的原因可能在于Threadpool本身的性质(稍后会再回过头来看)。

查看Threads完成所花费的时间。你的测试方法只能睡5秒,就是这样。现在另一个.43秒去了哪里?对,创建和销毁线程本身以及相关的开销包括上下文切换。 Threadpool有一个Threads队列,可以用来同时执行。这取决于Threadpool及其配置,以便在需要时创建和销毁额外的线程。当您在Threadpool中安排60个项目时,Threadpool可能不会创建60个线程来同时处理所有项目,而是使用其中的一个子项并处理每个线程的多个项目。由于您的测试方法只是休眠,这解释了线程花费的时间与使用Threadpool花费的时间之间的巨大差异。

由于TPL在内部使用Threadpool并且在您运行ThreadPool测试之前,因此可以合理地假设,在该阶段:Threadpool中可用的线程较少,但由于TPL的运行,创建了更多线程对于Threadpool,反过来,当你的Threadpool测试运行时,最初有更多线程可用,这解释了TPL和Threadpool之间的区别。

实际上,您希望尽可能多地使用Threadpool,尤其是对于计算操作。当您需要与外部资源同步(例如从Web下载内容)时,我建议不使用线程,而是使用.NET中可用的更高级异步选项之一来获取该特定资源。