What is threading and multi threading?

 

What is threading and multi threading?
What is Threading and Multithreading in Python?

Threading and multithreading are techniques in Python that allow you to execute multiple tasks
concurrently within a single process. This approach helps improve performance, especially in I/O-bound applications.

What is a Thread?

A thread is the smallest unit of a process that can be scheduled and executed by the operating system. By default, Python programs execute as a single-threaded process. However, the threading module allows you to create and manage multiple threads.

What is Multithreading?

Multithreading is the process of running multiple threads concurrently in a single program. It is particularly useful in:

  • Performing multiple I/O tasks (e.g., reading files, making network requests).
  • Keeping the application responsive (e.g., in GUI applications).

Note: Python's Global Interpreter Lock (GIL) may limit the benefits of multithreading in CPU-bound tasks. For such tasks, multiprocessing is a better alternative.


Threading in Python

Python provides the threading module to work with threads. Let’s explore its usage with examples.

Basic Example of Threading

The following code demonstrates how to create and start a thread in Python.


import threading
import time

def print_numbers():
    for i in range(1, 6):
        print(f"Number: {i}")
        time.sleep(1)

# Creating a thread
thread = threading.Thread(target=print_numbers)

# Starting the thread
thread.start()

# Main thread execution
print("Main thread is running...")

# Waiting for the thread to complete
thread.join()

print("Thread has finished execution!")

Output:


Main thread is running...
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
Thread has finished execution!


Multithreading with Multiple Threads

You can run multiple threads simultaneously using the threading module.


import threading
import time

def print_task(task_name):
    for i in range(1, 4):
        print(f"{task_name} - Step {i}")
        time.sleep(1)

# Creating multiple threads
thread1 = threading.Thread(target=print_task, args=("Task 1",))
thread2 = threading.Thread(target=print_task, args=("Task 2",))

# Starting the threads
thread1.start()
thread2.start()

# Waiting for both threads to complete
thread1.join()
thread2.join()

print("Both tasks completed!")

Output:


Task 1 - Step 1
Task 2 - Step 1
Task 1 - Step 2
Task 2 - Step 2
Task 1 - Step 3
Task 2 - Step 3
Both tasks completed!


Using Locks to Prevent Race Conditions

When multiple threads access shared resources, race conditions can occur. Python's threading module provides a Lock object to synchronize threads and avoid such issues.


import threading

balance = 0
lock = threading.Lock()

def deposit(amount):
    global balance
    for _ in range(1000000):
        with lock:  # Acquire and release the lock automatically
            balance += amount

def withdraw(amount):
    global balance
    for _ in range(1000000):
        with lock:
            balance -= amount

# Creating threads
thread1 = threading.Thread(target=deposit, args=(1,))
thread2 = threading.Thread(target=withdraw, args=(1,))

# Starting threads
thread1.start()
thread2.start()

# Waiting for threads to finish
thread1.join()
thread2.join()

print(f"Final Balance: {balance}")

Output:


Final Balance: 0


Real-World Example of Multithreading

Here’s an example of using multithreading to fetch data from multiple websites concurrently:


import threading
import requests

def fetch_url(url):
    response = requests.get(url)
    print(f"Fetched {url} with status: {response.status_code}")

urls = [
    "https://www.google.com",
    "https://www.python.org",
    "https://www.github.com"
]

# Creating threads for each URL
threads = [threading.Thread(target=fetch_url, args=(url,)) for url in urls]

# Starting threads
for thread in threads:
    thread.start()

# Waiting for threads to finish
for thread in threads:
    thread.join()


Advantages of Multithreading

  1. Concurrency: Threads allow multiple tasks to run concurrently.
  2. Responsiveness: Applications remain responsive while performing time-consuming tasks.
  3. Resource Sharing: Threads within a process share the same memory space.

Conclusion

Threading and multithreading in Python provide a powerful way to manage multiple tasks simultaneously, especially for I/O-bound applications. While Python’s GIL may limit multithreading for CPU-bound tasks, threading remains a vital tool for improving program efficiency in the right scenarios.

Comments