Tasks - more efficient that thread

Overview of thread

Drawback of threads

We had seen that threads provide a way execute code parallelly. When we execute blocks of code using thread, there is some work done for creation of the thread and deletion of the thread by the OS. This work done for creating a thread and deleting a thread is significant amount of work.

Task - improvement over thread

Tasks are higher order abstraction of threads. When executing a block of code using a task, the cost of creating of thread and deletion of the thread is not incurred. Only the productive work of the task is done.

Thread pool

.Net provides a thread pool. The thread pool is nothing but a collection of threads that are already created. When a piece of code is executed in a task, a thread is taken from the thread pool, and that piece of code is executed and post execution the thread is returned to the thread pool instead of destroying it.

This way new threads are not created and destroyed often removing the non-productive work.

Experimental Verification

Code

The below code takes an array of numbers and for each number it checks if the number is a prime number or not with a thread and a task. The total time spend by the thread and task is captured.

using System.Diagnostics;

List<int> inputNumbers = GetInputNumbers();

MeasureTime("Tasks", () => { CheckViaTasks(inputNumbers); });
Task.Delay(1000).Wait();
MeasureTime("Thread", () => { CheckViaThread(inputNumbers); });

static void MeasureTime(string name, Action action)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    action();
    sw.Stop();
    Console.WriteLine("Elapsed for {0} = {1} ms", name, sw.ElapsedMilliseconds);
}

static void CheckViaThread(List<int> inputNumbers)
{
    var threads = inputNumbers.Select(x =>
    {
        var thread = new Thread(() =>
        {
            var isPrime = IsPrime(x);
            //Console.WriteLine($"{x} is prime: {isPrime}");
        });
        thread.Start();
        return thread;
    }).ToList();

    foreach (var thread in threads)
    {
        thread.Join();
    }
}

static void CheckViaTasks(List<int> inputNumbers)
{
    Task.Run(() =>
    {
        var tasks = inputNumbers.Select(x =>
        {
            return Task.Run(() =>
            {
                var isPrime = IsPrime(x);
                //Console.WriteLine($"{x} is prime: {isPrime}");
            });
        }).ToList();

        Task.WaitAll(tasks.ToArray());

    }).Wait();
}

static bool IsPrime(int number)
{
    for (int i = 2; i < number - 1; i++)
    {
        var reminder = number % i;
        if (reminder == 0)
        {
            return false;
        }
    }
    return true;
}

static List<int> GetInputNumbers()
{
    int[] numbers = new int[] {
    104179, 102342, 104183, 104207,
    104233, 104239, 104243, 104281,
    104287, 104297, 104309, 104311,
    104323, 104327, 104347, 104369,
    104381, 104383, 104393, 104399,
    104417, 104231 };

    List<int> inputNumbers = new List<int>(numbers);
    for (int i = 0; i < 30; i++)
    {
        inputNumbers.AddRange(numbers);
    }

    return inputNumbers;
}

Output

The output shows it took only 60 milli seconds to check if the numbers were prime or not. But it took over 4840 milli seconds for threads to perform the same operations.

Inference from the Experiment

From the above experiment, we can infer that tasks are more efficient than threads.

Further reading

  • Task Parallel Library

Code Reference

This repo contains all the code that was used for this demonstration.