Multithreading - potatoscript/csharp GitHub Wiki

🥔 Multithreading in C# 🥔


🎯 What is Multithreading?

Imagine you are running a potato factory 🏭 where you:

  • 🥔 Peel the potatoes.
  • 🔪 Cut the potatoes.
  • 🍳 Fry the potatoes.
  • 🎁 Pack the French fries.

If you had only one worker, they would do everything one by one. It would take forever to finish! ⏳

✅ But if you hire 4 workers, they can do these tasks at the same time:

  • Worker 1: Peels the potatoes 🥔.
  • Worker 2: Cuts the potatoes 🔪.
  • Worker 3: Fries the potatoes 🍳.
  • Worker 4: Packs the fries 🎁.

Multithreading works just like that! It allows you to run multiple tasks at the same time, which makes your program faster and more efficient. ⚡🥔


🧠 How Does Multithreading Work?

A thread is like a worker who performs a task in the potato factory. 🥔👨‍🔧

Single-threaded program: Only one worker processes everything.
Multi-threaded program: Multiple workers handle tasks at the same time.


Why Use Multithreading?

Without multithreading:

  • 🐢 Everything happens one by one.
  • ⏰ It takes a lot of time.

With multithreading:

  • 🚀 Multiple tasks happen simultaneously.
  • 🎉 Your program runs faster and can handle multiple operations easily.

🥔 Basic Syntax of Multithreading in C#

To create a thread in C#, you use:

  • Thread class from System.Threading.
  • Start() to begin the thread.
  • Join() to wait for the thread to finish.

🎁 Simple Multithreading Example

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // Create two threads for different tasks
        Thread peelThread = new Thread(PeelPotatoes);
        Thread cutThread = new Thread(CutPotatoes);

        // Start the threads
        peelThread.Start();
        cutThread.Start();

        // Wait for both threads to complete
        peelThread.Join();
        cutThread.Join();

        Console.WriteLine("🍽️ All tasks finished!");
    }

    static void PeelPotatoes()
    {
        Console.WriteLine("🥔 Peeling potatoes...");
        Thread.Sleep(2000);  // Simulate 2 seconds of work
        Console.WriteLine("✅ Done peeling.");
    }

    static void CutPotatoes()
    {
        Console.WriteLine("🔪 Cutting potatoes...");
        Thread.Sleep(2000);  // Simulate 2 seconds of work
        Console.WriteLine("✅ Done cutting.");
    }
}

Explanation:

  • Thread – Creates a new thread.
  • Start() – Begins the thread.
  • Join() – Waits for the thread to finish.

🎁 Output:

🥔 Peeling potatoes...
🔪 Cutting potatoes...
✅ Done peeling.
✅ Done cutting.
🍽️ All tasks finished!

Benefit: Tasks happen simultaneously! No waiting! 🎉


🥔 Multithreading with Parameters

You can pass parameters to a thread using ParameterizedThreadStart.

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread thread = new Thread(new ParameterizedThreadStart(PeelPotatoes));
        thread.Start(5);  // Passing 5 potatoes to peel
        thread.Join();

        Console.WriteLine("🍽️ All potatoes peeled!");
    }

    static void PeelPotatoes(object numPotatoes)
    {
        int count = (int)numPotatoes;
        for (int i = 1; i <= count; i++)
        {
            Console.WriteLine($"🥔 Peeling potato {i}...");
            Thread.Sleep(1000);  // Simulate 1 second per potato
        }
        Console.WriteLine("✅ All potatoes peeled!");
    }
}

Explanation:

  • ParameterizedThreadStart – Allows passing parameters to the thread.
  • thread.Start(5) – Starts the thread with 5 potatoes.

🎁 Output:

🥔 Peeling potato 1...
🥔 Peeling potato 2...
🥔 Peeling potato 3...
🥔 Peeling potato 4...
🥔 Peeling potato 5...
✅ All potatoes peeled!
🍽️ All potatoes peeled!

🥔 Using Lambda Expressions with Threads

For simpler tasks, you can use lambda expressions to define thread behavior.

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // Create thread using lambda expression
        Thread thread = new Thread(() => 
        {
            Console.WriteLine("🥔 Peeling potatoes...");
            Thread.Sleep(2000);
            Console.WriteLine("✅ Done peeling.");
        });

        thread.Start();
        thread.Join();

        Console.WriteLine("🍽️ All tasks done!");
    }
}

Explanation:

  • ()=> { ... } – Defines the task for the thread.
  • Start() – Starts the thread.

🎁 Output:

🥔 Peeling potatoes...
✅ Done peeling.
🍽️ All tasks done!

🥔 Thread Safety and Locks

When multiple threads try to access the same data, it can cause conflicts and errors. This is called a race condition. 🏁🥔

✅ To prevent this, we use locks.


🔐 Using lock to Prevent Race Conditions

using System;
using System.Threading;

class Program
{
    static int potatoCount = 0;
    static object lockObject = new object();

    static void Main()
    {
        Thread thread1 = new Thread(AddPotatoes);
        Thread thread2 = new Thread(AddPotatoes);

        thread1.Start();
        thread2.Start();

        thread1.Join();
        thread2.Join();

        Console.WriteLine($"🥔 Total potatoes processed: {potatoCount}");
    }

    static void AddPotatoes()
    {
        for (int i = 0; i < 5; i++)
        {
            lock (lockObject)  // Lock to prevent conflicts
            {
                potatoCount++;
                Console.WriteLine($"🥔 Potato added! Total: {potatoCount}");
            }
            Thread.Sleep(500);
        }
    }
}

Explanation:

  • lock (lockObject) – Ensures only one thread modifies potatoCount at a time.
  • potatoCount++ – Safely adds to the potato count.

🎁 Output:

🥔 Potato added! Total: 1
🥔 Potato added! Total: 2
🥔 Potato added! Total: 3
🥔 Potato added! Total: 4
🥔 Potato added! Total: 5
🥔 Potato added! Total: 6
🥔 Potato added! Total: 7
🥔 Potato added! Total: 8
🥔 Potato added! Total: 9
🥔 Potato added! Total: 10
🥔 Total potatoes processed: 10

Benefit: No conflict between threads! 🎉


🥔 Thread Pooling in C#

When you need to run many short tasks, using Thread Pool is faster than creating new threads manually.

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Console.WriteLine("🥔 Starting Thread Pool...");

        for (int i = 1; i <= 5; i++)
        {
            ThreadPool.QueueUserWorkItem(ProcessPotato, i);
        }

        Thread.Sleep(3000);  // Wait for all threads to finish
        Console.WriteLine("🍽️ All tasks done!");
    }

    static void ProcessPotato(object state)
    {
        int potatoNum = (int)state;
        Console.WriteLine($"🥔 Processing potato {potatoNum}...");
        Thread.Sleep(1000);
        Console.WriteLine($"✅ Potato {potatoNum} done!");
    }
}

Explanation:

  • ThreadPool.QueueUserWorkItem() – Queues a task to be executed by available threads in the thread pool.

🎁 Output:

🥔 Starting Thread Pool...
🥔 Processing potato 1...
🥔 Processing potato 2...
🥔 Processing potato 3...
🥔 Processing potato 4...
🥔 Processing potato 5...
✅ Potato 1 done!
✅ Potato 2 done!
✅ Potato 3 done!
✅ Potato 4 done!
✅ Potato 5 done!
🍽️ All tasks done!

🥔 Best Practices for Multithreading

  1. Use Thread Pool: For short tasks.
  2. Avoid Excessive Threads: Too many threads slow down the system.
  3. 🔐 Use Locks: Prevent conflicts in shared data.
  4. Avoid Thread.Sleep(): Use Task.Delay() in async code.
  5. 📚 Use Task and async/await for better performance.

🥔 Summary of Multithreading

🌟 Feature 📝 Description
Thread Creation Create multiple threads for tasks.
🏎️ Faster Execution Run tasks simultaneously.
🔐 Thread Safety Use lock to prevent data conflicts.
Thread Pool Use thread pool for short tasks.
🥔 Parameterized Threads Pass data to threads.