Skip to main content

This recipe will show how a thread pool works with many asynchronous operations, and how it is different from creating many separate threads. Learn how a thread pool works with many asynchronous operations and how it is different from creating many separate threads.

using System;
using System.Diagnostics;
using System.Threading;

//
// Thread Pool and the Degree of Parallelism
//
// This recipe will show how a thread pool works with many asynchronous operations,
// and how it is different from creating many separate threads. Learn how a thread
// pool works with many asynchronous operations and how it is different from
// creating many separate threads.
//
// How It Works...
//
// When the main program starts, we create many different threads and run an
// operation on each one of them. This operation prints out a thread ID and
// blocks a thread for 100 milliseconds. As a result, we create 500 threads,
// which run all of those operations in parallel. The total time on my machine
// is about 300 milliseconds, but we consume many operating system resources for
// all those threads.
//
// Then, we follow the same procedure, but instead of creating a thread for each
// operation, we post them on a thread pool. After this, the thread pool starts
// to serve these operations; it begins to create more threads near the end, but
// still, it takes much more time, about 12 seconds on my machine. We save
// memory and threads for an operating system usage but pay with an execution
// time for it.
//

namespace Chapter3.Recipe3
{
    class Program
    {
        static void Main(string[] args)
        {
            const int numberOfOperations = 500;
            var sw = new Stopwatch();
            sw.Start();
            UseThreads(numberOfOperations);
            sw.Stop();
            Console.WriteLine("Execution time using threads: {0}", sw.ElapsedMilliseconds);

            sw.Reset();
            sw.Start();
            UseThreadPool(numberOfOperations);
            sw.Stop();
            Console.WriteLine("Execution time using threads: {0}", sw.ElapsedMilliseconds);
        }

        static void UseThreads(int numberOfOperations)
        {
            using (var countdown = new CountdownEvent(numberOfOperations))
            {
                Console.WriteLine("Scheduling work by creating threads");
                for (int i = 0; i < numberOfOperations; i++)
                {
                    var thread = new Thread(() =>
                    {
                        Console.Write("{0},", Thread.CurrentThread.ManagedThreadId);
                        Thread.Sleep(TimeSpan.FromSeconds(0.1));
                        countdown.Signal();
                    });
                    thread.Start();
                }
                countdown.Wait();
                Console.WriteLine();
            }
        }

        static void UseThreadPool(int numberOfOperations)
        {
            using (var countdown = new CountdownEvent(numberOfOperations))
            {
                Console.WriteLine("Starting work on a threadpool");
                for (int i = 0; i < numberOfOperations; i++)
                {
                    ThreadPool.QueueUserWorkItem( _ =>
                    {
                        Console.Write("{0},", Thread.CurrentThread.ManagedThreadId);
                        Thread.Sleep(TimeSpan.FromSeconds(0.1));
                        countdown.Signal();
                    });
                }
                countdown.Wait();
                Console.WriteLine();
            }
        }
    }
}