Why there is so much performance diffrence between

2019-08-30 06:53发布

Here i attached my example that i used for performance test. Why there is so much diffrence between all this ? (This is sample console application)

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();
        }

    }

Please provide your suggetion on this.

Here is sample output that i got.

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楼-- · 2019-08-30 07:03

You're test case is far from solid to begin with. When you perform actual computation work within the threadWork method, you'll find that the results are very different. TPL uses the threadpool internally so it's a matter of Threadpool vs Threads. The reason for the TPL to be so different compared to the Threadpool is likely in the nature of the Threadpool itself (will come back on that one later).

Look at the time it took for Threads to complete. Your test method only sleeps for 5 seconds, that's it. Now where did the other .43 second go to? Right, to the creation and destruction of the Thread itself and the associated overhead including context switching. The Threadpool has a queue of Threads that can be used to execute simultaneously. It's up to the Threadpool and it's configuration to create and destroy extra threads whenever it deems needed. When you schedule 60 items in the Threadpool , the Threadpool wont likely create 60 threads to handle all items simultaneously but rather use a sub amount of that and handle multiple items per thread. Since your test method is only sleeping, this explains the big difference between the time spent with threads and the time spent with the Threadpool.

Since TPL uses the Threadpool internally and before you're ThreadPool test ran, it is logical to assume that, at that stage: less threads were available in the Threadpool, but due to the run of the TPL, more threads were created for the Threadpool so in turn, when your Threadpool test ran, there were more threads available initially which explains the difference between TPL and the Threadpool.

Practically, you want to use the Threadpool as much as possible, especially for computational operations. When you need to synchronize with an external resource like downloading something from the web, i recommend not using a thread but one of the more advanced async options available in .NET for getting that particular resource.

查看更多
登录 后发表回答